Bitcoin-Secp256k1

 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

# interacting directly with libsecp256k1. All of these methods are private and
# subject to change. They are used internally to deliver high level API below.

require XSLoader;
XSLoader::load('Bitcoin::Secp256k1', $Bitcoin::Secp256k1::VERSION);

# HIGH LEVEL API
# These methods are implemented in Perl and deliver more convenient API to
# interact with. They are stable and public.

sub verify_private_key
{
	my ($self, $private_key) = @_;

	return $self->_verify_privkey($private_key);
}

sub verify_public_key
{
	my ($self, $public_key) = @_;
	local $@;

	my $success = eval {
		$self->_pubkey($public_key);
		1;
	};

	return !!$success;
}

sub create_public_key
{
	my ($self, $private_key) = @_;

	$self->_create_pubkey($private_key);
	return $self->_pubkey;
}

sub normalize_signature
{
	my ($self, $signature) = @_;

	$self->_signature($signature);
	$self->_normalize;

lib/Bitcoin/Secp256k1.pm  view on Meta::CPAN

sub compress_public_key
{
	my ($self, $public_key, $compressed) = @_;
	$compressed //= !!1;

	return $self->_pubkey($public_key, $compressed);
}

sub sign_message
{
	my ($self, $private_key, $message) = @_;

	return $self->sign_digest($private_key, sha256(sha256($message)));
}

sub sign_message_schnorr
{
	my ($self, $private_key, $message) = @_;

	return $self->sign_digest_schnorr($private_key, sha256($message));
}

sub sign_digest
{
	my ($self, $private_key, $digest) = @_;

	$self->_sign($private_key, $digest);
	return $self->_signature;
}

sub sign_digest_schnorr
{
	my ($self, $private_key, $digest) = @_;

	$self->_sign_schnorr($private_key, $digest);
	return $self->_signature_schnorr;
}

sub verify_message
{
	my ($self, $public_key, $signature, $message) = @_;

	return $self->verify_digest($public_key, $signature, sha256(sha256($message)));
}

lib/Bitcoin/Secp256k1.pm  view on Meta::CPAN

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) = @_;

	return $self->_privkey_negate($private_key);
}

sub xonly_public_key
{
	my ($self, $public_key) = @_;

	$self->_pubkey($public_key);
	$self->_convert_pubkey_xonly;

	return $self->_xonly_pubkey;

lib/Bitcoin/Secp256k1.pm  view on Meta::CPAN

sub add_public_key
{
	my ($self, $public_key, $tweak) = @_;

	$self->_pubkey($public_key);
	$self->_pubkey_add($tweak);

	return $self->_pubkey;
}

sub add_private_key
{
	my ($self, $private_key, $tweak) = @_;

	return $self->_privkey_add($private_key, $tweak);
}

sub multiply_public_key
{
	my ($self, $public_key, $tweak) = @_;

	$self->_pubkey($public_key);
	$self->_pubkey_mul($tweak);

	return $self->_pubkey;
}

sub multiply_private_key
{
	my ($self, $private_key, $tweak) = @_;

	return $self->_privkey_mul($private_key, $tweak);
}

sub combine_public_keys
{
	my ($self, @public_keys) = @_;

	$self->_clear;
	foreach my $pub (@public_keys) {
		$self->_pubkey($pub);
		$self->_push_pubkey;

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);

=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 a
chance 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_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 verify_message

	$valid = $secp256k1->verify_message($public_key, $signature, $message)

Verifies C<$signature> (DER-encoded, bytestring) of C<$message> (bytestring of

lib/Bitcoin/Secp256k1.pm  view on Meta::CPAN

Same as L</verify_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 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)

t/api.t  view on Meta::CPAN

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';

t/api.t  view on Meta::CPAN


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' => sub {
	my @to_combine = (
		pack('H*', '0311ab47c9252066f0ca5946d70c3aaac1486d65969b90cd57207476963c9f9af3'),
		pack('H*', '0260213f6d967636c54d8845c23098e0f63d906b7903d23692efa155a155eda169'),
	);
	my $combined_pubkey = pack 'H*', '0255c3386d6833d5e1ad6d863afc1cf5d8ffdc0ebc78e4241e845a6c2cbd78157b';

	is $secp->combine_public_keys($t{pubkey}, @to_combine), $combined_pubkey, 'combined pubkey ok';

t/author-leaks.t  view on Meta::CPAN

use Bitcoin::Secp256k1;

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 $public_key = $secp->create_public_key($private_key);
	$public_key = $secp->compress_public_key($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);
}
calls => 1000, 'construction/destruction of Bitcoin::Secp256k1 does not leak';

done_testing;

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) };



( run in 0.268 second using v1.01-cache-2.11-cpan-4d50c553e7e )