Tezex.Crypto.BLS (tezex v3.2.0)
View SourcePure Elixir BLS12-381 cryptographic operations for Tezos tz4 addresses.
This module provides BLS functionality:
- Private key operations (from 32-byte seeds or HKDF key generation)
- Public key derivation (48-byte compressed G1 format)
- Message signing and verification with multiple ciphersuites
- Serialization/deserialization compatible with Tezos formats
- Support for message augmentation, basic, and proof-of-possession schemes
Based on the BLS12-381 curve with IETF standard ciphersuites:
- G2Basic: BLSSIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL
- G2MessageAugmentation: BLSSIG_BLS12381G2_XMD:SHA-256_SSWU_RO_AUG (default)
- G2ProofOfPossession: BLSSIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP
- MinPk variant (48-byte public keys, 96-byte signatures)
- Hash-to-curve with SSWU mapping
Partly ported from pytezos@439bafada8f063f9643c789f6154d4646674d66a
pytezos / MIT License / (c) 2020 Baking Bad / (c) 2018 Arthur Breitman and py_ecc@04151f01f59f902ab932a51e0ca0ebce3883fc51 py_ecc / MIT License / (c) (c) 2015 Vitalik Buterin
Summary
Functions
Returns the supported BLS ciphersuites and their domain separation tags.
Deserializes a BLS private key from bytes.
Converts a hex string to binary, handling both uppercase and lowercase.
Creates a BLS private key from a secret exponent (integer or bytes).
Creates a new BLS private key from a 32-byte seed.
Derives the BLS public key from the private key. Returns a 48-byte compressed G1 point.
Generates a BLS private key using HKDF key generation (IETF standard).
Generates a proof-of-possession for a BLS public key.
Verifies a proof-of-possession for a BLS public key.
Serializes a BLS private key to bytes.
Signs a message with the BLS private key using the specified ciphersuite. Returns a 96-byte signature.
Returns the expected sizes for BLS keys and signatures.
Converts binary to hex string (lowercase).
Validates a BLS public key to ensure it's a valid G1 point. Infinity points are rejected for public keys .
Validates a BLS signature to ensure it's a valid G2 point. Infinity points are allowed for signatures (unlike public keys).
Verifies a BLS signature against a message and public key using the specified ciphersuite.
Types
@type ciphersuite() :: :basic | :message_augmentation | :proof_of_possession
@type key_info() :: binary()
@type public_key() :: binary()
@type signature() :: binary()
@type t() :: %Tezex.Crypto.BLS{secret_key: Tezex.Crypto.BLS.Fr.t()}
Functions
@spec ciphersuites() :: %{ basic: String.t(), message_augmentation: String.t(), proof_of_possession: String.t(), pop_tag: String.t() }
Returns the supported BLS ciphersuites and their domain separation tags.
Deserializes a BLS private key from bytes.
Converts a hex string to binary, handling both uppercase and lowercase.
Creates a BLS private key from a secret exponent (integer or bytes).
Examples
iex> secret_bytes = :crypto.strong_rand_bytes(32)
iex> {:ok, bls} = Tezex.Crypto.BLS.from_secret_exponent(secret_bytes)
iex> byte_size(Tezex.Crypto.BLS.serialize_secret_key(bls))
32
Creates a new BLS private key from a 32-byte seed.
This matches octez-client behavior by using the seed directly as the scalar, without any key derivation function.
Examples
iex> seed = :crypto.strong_rand_bytes(32)
iex> {:ok, bls} = Tezex.Crypto.BLS.from_seed(seed)
iex> byte_size(Tezex.Crypto.BLS.Fr.to_bytes(bls.secret_key))
32
@spec get_public_key(t()) :: public_key()
Derives the BLS public key from the private key. Returns a 48-byte compressed G1 point.
Examples
iex> seed = :crypto.strong_rand_bytes(32)
iex> {:ok, bls} = Tezex.Crypto.BLS.from_seed(seed)
iex> pubkey = Tezex.Crypto.BLS.get_public_key(bls)
iex> byte_size(pubkey)
48
Generates a BLS private key using HKDF key generation (IETF standard).
This follows the KeyGen algorithm from the IETF BLS specification.
Parameters
ikm
: Input key material (typically 32+ bytes of entropy)key_info
: Optional key info for domain separation (default: empty)
Examples
iex> ikm = :crypto.strong_rand_bytes(32)
iex> {:ok, bls} = Tezex.Crypto.BLS.key_gen(ikm)
iex> is_struct(bls, Tezex.Crypto.BLS)
true
Generates a proof-of-possession for a BLS public key.
This proves that the holder knows the corresponding private key, following the IETF BLS proof-of-possession specification.
@spec pop_verify(public_key(), signature()) :: boolean()
Verifies a proof-of-possession for a BLS public key.
Serializes a BLS private key to bytes.
@spec sign(t(), binary(), ciphersuite()) :: signature()
Signs a message with the BLS private key using the specified ciphersuite. Returns a 96-byte signature.
Ciphersuites
:message_augmentation
(default): Message augmented with public key (most secure):basic
: Basic BLS signature (requires message uniqueness for aggregation):proof_of_possession
: Proof-of-possession scheme
Examples
iex> seed = :crypto.strong_rand_bytes(32)
iex> {:ok, bls} = Tezex.Crypto.BLS.from_seed(seed)
iex> message = "test message"
iex> signature = Tezex.Crypto.BLS.sign(bls, message)
iex> byte_size(signature)
96
@spec sizes() :: %{ secret_key: pos_integer(), public_key: pos_integer(), signature: pos_integer() }
Returns the expected sizes for BLS keys and signatures.
Converts binary to hex string (lowercase).
@spec validate_public_key(public_key()) :: boolean()
Validates a BLS public key to ensure it's a valid G1 point. Infinity points are rejected for public keys .
Validates a BLS signature to ensure it's a valid G2 point. Infinity points are allowed for signatures (unlike public keys).
@spec verify(signature(), binary(), public_key(), ciphersuite()) :: boolean()
Verifies a BLS signature against a message and public key using the specified ciphersuite.
Ciphersuites
:message_augmentation
(default): Message augmented with public key:basic
: Basic BLS signature:proof_of_possession
: Proof-of-possession scheme
Examples
iex> seed = :crypto.strong_rand_bytes(32)
iex> {:ok, bls} = Tezex.Crypto.BLS.from_seed(seed)
iex> message = "test message"
iex> pubkey = Tezex.Crypto.BLS.get_public_key(bls)
iex> signature = Tezex.Crypto.BLS.sign(bls, message)
iex> Tezex.Crypto.BLS.verify(signature, message, pubkey)
true