The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Bitcoin::Secp256k1 - Perl interface to libsecp256k1

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

DESCRIPTION

This module implements XS routines that allow accessing common elliptic curve operations on secp256k1 curve using Perl code. It requires libsecp256k1 to be installed on the system, and will try to detect and install it automatically using Alien::libsecp256k1.

INTERFACE

Attributes

None - object is a blessed readonly scalar reference with a memory address of a C structure. As such, it does not contain any attributes accessible directly from Perl.

Methods

new

        $secp256k1 = Bitcoin::Secp256k1->new()

Object constructor. All methods in this package require this object to work properly. It accepts no arguments.

verify_private_key

        $valid = $secp256k1->verify_private_key($private_key)

Checks whether bytestring $private_key is a valid private key. Private key is valid if its length is exactly 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.

create_public_key

        $public_key = $secp256k1->create_public_key($private_key)

Creates a public key from a bytestring $private_key and returns a bytestring $public_key. $private_key must have exact length of 32.

The public key is always returned in compressed form, use "compress_public_key" to get uncompressed form.

normalize_signature

        $signature = $secp256k1->normalize_signature($signature)

Performs signature normalization of $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.

Signature normalization is important because of Bitcoin protocol rules. Normally, Bitcoin will reject transactions with malleable signatures. This module will only emit a warning if you try to verify a signature that is not normalized.

This method lets you both detect whether the signature was malleable and fix it to avoid a warning if needed.

compress_public_key

        $public_key = $secp256k1->compress_public_key($public_key, $want_compressed = !!1)

Changes the compression form of bytestring $public_key. If $want_compressed is a true value (or omitted / undef), method will return the key in compressed (default) form. If it is a false value, $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).

sign_message

        $signature = $secp256k1->sign_message($private_key, $message)

Signs $message, which may be a bytestring of any length, with $private_key, which must be a bytestring of length 32. Returns DER-encoded $signature as a bytestring.

$message is first hashed with double SHA256 (known an HASH256 in Bitcoin) before passing it to signing algorithm (which expects length 32 bytestrings).

This method always produces normalized, deterministic signatures suitable to use inside a Bitcoin transaction.

sign_digest

        $signature = $secp256k1->sign_digest($private_key, $message_digest)

Same as "sign_message", but it does not perform double SHA256 on its input. Because of that, $message_digest must be a bytestring of length 32.

verify_message

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

Verifies $signature (DER-encoded, bytestring) of $message (bytestring of any length) against $public_key (compressed or uncompressed, bytestring). Returns true if verification is successful.

$message is first hashed with double SHA256 (known an HASH256 in Bitcoin) before passing it to verification algorithm (which expects length 32 bytestrings).

Raises a warning if $siganture is not normalized. It is recommended to perform signature normalization using "normalize_signature" first and either accept or reject malleable signatures explicitly.

verify_digest

        $valid = $secp256k1->verify_digest($public_key, $signature, $message_digest)

Same as "verify_message", but it does not perform double SHA256 on its input. Because of that, $message_digest must be a bytestring of length 32.

negate_private_key

        $negated_private_key = $secp256k1->negated_private_key($private_key)

Negates a private key and returns it.

negate_public_key

        $negated_public_key = $secp256k1->negate_public_key($public_key)

Negates a public key and returns it.

add_private_key

        $tweaked = $secp256k1->add_private_key($private_key, $tweak)

Add a $tweak (bytestring of length 32) to $private_key (bytestring of length 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.

add_public_key

        $tweaked = $secp256k1->add_public_key($public_key, $tweak)

Add a $tweak (bytestring of length 32) to $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.

multiply_private_key

        $tweaked = $secp256k1->multiply_private_key($private_key, $tweak)

Same as "add_private_key", but performs multiplication instead of addition.

multiply_public_key

        $tweaked = $secp256k1->multiply_public_key($public_key, $tweak)

Same as "add_public_key", but performs multiplication instead of addition.

IMPLEMENTATION

The module consists of two layers:

  • High-level API, which consists of public, stable methods. These methods should deliver most of the possible use cases for the library, but some paths may not be covered. All of these methods simply accept and return values without storing anything inside the object.

  • Low-level API, which is implemented in XS and private. It interacts directly with libsecp256k1 and is storing some intermediate state (but never the private key) in a blessed C structure. It covers all of library's functions which are valuable in Perl's context. Its existence is only significant to the author and the contributors.

    Notable exceptions are the constructor "new" and the destructor, which are also part of the low-level API, yet public.

The module also needs a cryptographically-secure source of pseudo-randomness to deliver the highest level of security. It will try to obtain it from CryptX or Bytes::Random::Secure. If none of these modules is installed, a warning will be issued every time a constructor is called. The library will continue to work as intended, but randomization is a security feature which protects against some types of attacks. Refer to libsecp256k1 documentation for details.

TODO

This module currently covers most usage paths of the base libsecp256k1. In the future, new methods to also cover some of its optional modules may be introduces, most notably the Schnorr module.

CAVEATS

Documentation of libsecp256k1 recommends keeping secrets on the stack (not the heap) and erasing them manually after they are no longer used. This is impossible in Perl, as it gives programmer no control over memory allocation. This library does not usually clear the secret key memory by overwriting it with zeros (unless it explicitly copied the secret to a new buffer). If you need this level of security, you should probably use libsecp256k1 directly in C code.

SEE ALSO

Alien::libsecp256k1

Bitcoin::Crypto

AUTHOR

Bartosz Jarzyna <bbrtj.pro@gmail.com>

COPYRIGHT AND LICENSE

Copyright (C) 2024 by Bartosz Jarzyna

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.