# `Tezex.Crypto.BLS`
[🔗](https://github.com/objkt-com/tezex/blob/v4.0.0/lib/crypto/bls.ex#L1)

Pure 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: BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_
- G2MessageAugmentation: BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_AUG_ (default)
- G2ProofOfPossession: BLS_SIG_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

# `ciphersuite`

```elixir
@type ciphersuite() :: :basic | :message_augmentation | :proof_of_possession
```

# `key_info`

```elixir
@type key_info() :: binary()
```

# `public_key`

```elixir
@type public_key() :: binary()
```

# `signature`

```elixir
@type signature() :: binary()
```

# `t`

```elixir
@type t() :: %Tezex.Crypto.BLS{secret_key: Tezex.Crypto.BLS.Fr.t()}
```

# `ciphersuites`

```elixir
@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.

# `deserialize_secret_key`

```elixir
@spec deserialize_secret_key(binary()) :: {:ok, t()} | {:error, :invalid_key}
```

Deserializes a BLS private key from bytes.

# `from_hex`

```elixir
@spec from_hex(String.t()) :: {:ok, binary()} | {:error, :invalid_hex}
```

Converts a hex string to binary, handling both uppercase and lowercase.

# `from_secret_exponent`

```elixir
@spec from_secret_exponent(binary() | integer()) ::
  {:ok, t()} | {:error, :invalid_secret}
```

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

# `from_seed`

```elixir
@spec from_seed(binary()) :: {:ok, t()} | {:error, :invalid_seed}
```

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

# `get_public_key`

```elixir
@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

# `key_gen`

```elixir
@spec key_gen(binary(), key_info()) :: {:ok, t()} | {:error, :invalid_ikm}
```

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

# `pop_prove`

```elixir
@spec pop_prove(t()) :: signature()
```

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.

# `pop_verify`

```elixir
@spec pop_verify(public_key(), signature()) :: boolean()
```

Verifies a proof-of-possession for a BLS public key.

# `serialize_secret_key`

```elixir
@spec serialize_secret_key(t()) :: binary()
```

Serializes a BLS private key to bytes.

# `sign`

```elixir
@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

# `sizes`

```elixir
@spec sizes() :: %{
  secret_key: pos_integer(),
  public_key: pos_integer(),
  signature: pos_integer()
}
```

Returns the expected sizes for BLS keys and signatures.

# `to_hex`

```elixir
@spec to_hex(binary()) :: String.t()
```

Converts binary to hex string (lowercase).

# `validate_public_key`

```elixir
@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 .

# `validate_signature`

```elixir
@spec validate_signature(signature()) :: boolean()
```

Validates a BLS signature to ensure it's a valid G2 point.
Infinity points are allowed for signatures (unlike public keys).

# `verify`

```elixir
@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

---

*Consult [api-reference.md](api-reference.md) for complete listing*
