libdecaf_decaf448 (libdecaf v3.0.0)

View Source

Decaf448 prime-order group arithmetic, backed directly by libdecaf_nif.

Implements the group operations and element derivation specified in RFC 9496. Points use the canonical 56-byte Decaf448 encoding. Scalars are unsigned little-endian integers in exactly 56 bytes, strictly smaller than the group order l:

l = 2^446 - 13818066809895115352007386748515426880336692474882178609894547503885

Point and scalar binaries have the same length but different meanings. Use point_is_valid/1 and scalar_is_canonical/1 to validate untrusted inputs. The Erlang binary types describe their size; native operations validate their contents. Arithmetic accepts the identity point and scalar zero, and raises badarg for invalid operands. scalar_invert/1 returns error for zero. These encodings are specific to this group; EdDSA and X25519/X448 keys use different formats and cannot be substituted for group points or scalars.

Use scalar_reduce/1 to encode an integer modulo l, or scalar_random/0 to generate a scalar with negligible reduction bias. Scalars are not clamped. For example, multiplication by three agrees with repeated point addition:

Three = libdecaf_decaf448:scalar_reduce(<<3>>),
Base = libdecaf_decaf448:base(),
Point = libdecaf_decaf448:base_scalarmul(Three),
Point = libdecaf_decaf448:point_add(Base, libdecaf_decaf448:point_double(Base)).

point_from_hash/1 consumes exactly 112 bytes of prepared input; it does not hash a message itself. Use a protocol-defined, domain-separated hash/XOF construction to derive those bytes. point_from_hash_nonuniform/1 exposes the single-map operation for protocols that specifically need it.

precompute/1 creates an immutable native multiplication table for reuse with precomputed_scalarmul/2. Tables belong to this group, can be shared between processes, and are released automatically when no references remain. Ordinary operations return canonical binaries and do not mutate their inputs.

Summary

Types

A canonical 56-byte Decaf448 point encoding, including the identity. Use point_is_valid/1 to check the encoded contents at runtime.

An immutable native multiplication table for a Decaf448 point. Created by precompute/1 and usable only with this group's precomputed_scalarmul/2.

A canonical 56-byte unsigned little-endian integer smaller than l. Zero is valid. Use scalar_is_canonical/1 to validate an encoding or scalar_reduce/1 to reduce an integer into this field.

Functions

Return the canonical Decaf448 generator from RFC 9496.

Multiply the canonical Decaf448 generator by Scalar.

Return the Decaf448 identity element, encoded as 56 zero bytes.

Return the canonical encoding of Left + Right.

Return Point + Point in canonical encoding.

Return Scalar1 * Point1 + Scalar2 * Point2 in canonical encoding.

Return whether two encoded points represent the same group element.

Derive a Decaf448 element from exactly 112 bytes using RFC 9496.

Apply the upstream single map to exactly 56 bytes.

Return whether Point is a valid canonical 56-byte Decaf448 encoding.

Return the additive inverse of Point in canonical encoding.

Return Scalar * Point in canonical encoding.

Return the canonical encoding of Left - Right.

Build an immutable native multiplication table for Point.

Multiply the point stored in Precomputed by Scalar.

Return (Left + Right) mod l, where l is the group order.

Divide Scalar by two modulo the group order.

Return the multiplicative inverse of Scalar modulo the group order.

Return whether Scalar is a canonical 56-byte little-endian scalar.

Return (Left * Right) mod l, where l is the group order.

Return -Scalar mod l, where l is the group order.

Generate a canonical scalar using crypto:strong_rand_bytes/1.

Reduce an arbitrary-length unsigned little-endian integer modulo the group order.

Return (Left - Right) mod l, where l is the group order.

Types

point()

-type point() :: <<_:448>>.

A canonical 56-byte Decaf448 point encoding, including the identity. Use point_is_valid/1 to check the encoded contents at runtime.

precomputed()

-opaque precomputed()

An immutable native multiplication table for a Decaf448 point. Created by precompute/1 and usable only with this group's precomputed_scalarmul/2.

scalar()

-type scalar() :: <<_:448>>.

A canonical 56-byte unsigned little-endian integer smaller than l. Zero is valid. Use scalar_is_canonical/1 to validate an encoding or scalar_reduce/1 to reduce an integer into this field.

Functions

base()

-spec base() -> Result when Result :: point().

Return the canonical Decaf448 generator from RFC 9496.

Use this point for interoperable generator multiplication. The base-scalar multiplication operation uses its built-in precomputed table.

base_scalarmul(Scalar)

-spec base_scalarmul(Scalar) -> Result when Scalar :: scalar(), Result :: point().

Multiply the canonical Decaf448 generator by Scalar.

Uses the native library's built-in precomputed generator table. The scalar must already be canonical; otherwise raises badarg.

One = libdecaf_decaf448:scalar_reduce(<<1>>),
Base = libdecaf_decaf448:base(),
Base = libdecaf_decaf448:base_scalarmul(One).

identity()

-spec identity() -> Result when Result :: point().

Return the Decaf448 identity element, encoded as 56 zero bytes.

Adding this point leaves the other point unchanged. Multiplication by zero and multiplication of the identity by any scalar both return the identity.

point_add(Left, Right)

-spec point_add(Left, Right) -> Result when Left :: point(), Right :: point(), Result :: point().

Return the canonical encoding of Left + Right.

Both operands must be valid point encodings; otherwise raises badarg. Adding a point to its negation returns the identity.

point_double(Point)

-spec point_double(Point) -> Result when Point :: point(), Result :: point().

Return Point + Point in canonical encoding.

This is equivalent to multiplication by the scalar two. An invalid point encoding raises badarg.

point_double_scalarmul(Point1, Scalar1, Point2, Scalar2)

-spec point_double_scalarmul(Point1, Scalar1, Point2, Scalar2) -> Result
                                when
                                    Point1 :: point(),
                                    Scalar1 :: scalar(),
                                    Point2 :: point(),
                                    Scalar2 :: scalar(),
                                    Result :: point().

Return Scalar1 * Point1 + Scalar2 * Point2 in canonical encoding.

Computes the same result as two scalar multiplications followed by addition. Both points must be valid and both scalars canonical; otherwise raises badarg.

point_eq(Left, Right)

-spec point_eq(Left, Right) -> Result when Left :: point(), Right :: point(), Result :: boolean().

Return whether two encoded points represent the same group element.

Both operands are decoded and validated. Invalid encodings raise badarg; false means that two valid points differ.

point_from_hash(UniformBytes)

-spec point_from_hash(UniformBytes) -> Result when UniformBytes :: binary(), Result :: point().

Derive a Decaf448 element from exactly 112 bytes using RFC 9496.

The two halves are mapped separately and their points are added. Uniform input gives an approximately uniform group element. This operation does not hash a message: use a protocol-defined, domain-separated hash or XOF to prepare the input. Wrong-sized binaries and non-binaries raise badarg.

For a random group element, prepare uniformly random input:

UniformBytes = crypto:strong_rand_bytes(112),
Point = libdecaf_decaf448:point_from_hash(UniformBytes),
true = libdecaf_decaf448:point_is_valid(Point).

point_from_hash_nonuniform(Bytes)

-spec point_from_hash_nonuniform(Bytes) -> Result when Bytes :: binary(), Result :: point().

Apply the upstream single map to exactly 56 bytes.

This is one half of the two-map RFC 9496 derivation. Its output has a different distribution, so use it only when a protocol requires the single map. Wrong-sized binaries and non-binaries raise badarg.

point_is_valid(Point)

-spec point_is_valid(Point) -> Result when Point :: term(), Result :: boolean().

Return whether Point is a valid canonical 56-byte Decaf448 encoding.

The identity is valid. Wrong-sized binaries, noncanonical encodings, and non-binary terms return false instead of raising badarg.

point_negate(Point)

-spec point_negate(Point) -> Result when Point :: point(), Result :: point().

Return the additive inverse of Point in canonical encoding.

The input point and its negation add to the identity. An invalid point encoding raises badarg.

point_scalarmul(Point, Scalar)

-spec point_scalarmul(Point, Scalar) -> Result
                         when Point :: point(), Scalar :: scalar(), Result :: point().

Return Scalar * Point in canonical encoding.

The point must be valid and the scalar must already be canonical. Invalid operands raise badarg; zero and the identity are accepted.

point_sub(Left, Right)

-spec point_sub(Left, Right) -> Result when Left :: point(), Right :: point(), Result :: point().

Return the canonical encoding of Left - Right.

Both operands must be valid point encodings; otherwise raises badarg. Subtracting a point from itself returns the identity.

precompute(Point)

-spec precompute(Point) -> Result when Point :: point(), Result :: precomputed().

Build an immutable native multiplication table for Point.

Reuse the table when multiplying the same point by many scalars. The resource can be shared between processes and is released automatically when no references remain. It is specific to Decaf448; invalid points raise badarg.

Point = libdecaf_decaf448:point_double(libdecaf_decaf448:base()),
Table = libdecaf_decaf448:precompute(Point),
Scalar = libdecaf_decaf448:scalar_reduce(<<7>>),
Product = libdecaf_decaf448:point_scalarmul(Point, Scalar),
Product = libdecaf_decaf448:precomputed_scalarmul(Table, Scalar).

precomputed_scalarmul(Precomputed, Scalar)

-spec precomputed_scalarmul(Precomputed, Scalar) -> Result
                               when Precomputed :: precomputed(), Scalar :: scalar(), Result :: point().

Multiply the point stored in Precomputed by Scalar.

The result matches ordinary point-scalar multiplication; the table is unchanged. A resource from another group, a non-table term, or a noncanonical Decaf448 scalar raises badarg.

scalar_add(Left, Right)

-spec scalar_add(Left, Right) -> Result when Left :: scalar(), Right :: scalar(), Result :: scalar().

Return (Left + Right) mod l, where l is the group order.

Both operands must already be canonical scalars; otherwise raises badarg. The result is canonically encoded.

scalar_halve(Scalar)

-spec scalar_halve(Scalar) -> Result when Scalar :: scalar(), Result :: scalar().

Divide Scalar by two modulo the group order.

This is multiplication by the modular inverse of two, rather than integer division. Adding the result to itself recovers the input, including for odd scalars. A noncanonical scalar raises badarg.

One = libdecaf_decaf448:scalar_reduce(<<1>>),
Half = libdecaf_decaf448:scalar_halve(One),
One = libdecaf_decaf448:scalar_add(Half, Half).

scalar_invert(Scalar)

-spec scalar_invert(Scalar) -> Result when Scalar :: scalar(), Result :: scalar() | error.

Return the multiplicative inverse of Scalar modulo the group order.

Multiplying a nonzero scalar by its inverse gives the canonical scalar one. Zero has no inverse and returns error. Noncanonical scalars raise badarg.

Scalar = libdecaf_decaf448:scalar_reduce(<<7>>),
Inverse = libdecaf_decaf448:scalar_invert(Scalar),
One = libdecaf_decaf448:scalar_reduce(<<1>>),
One = libdecaf_decaf448:scalar_mul(Scalar, Inverse),
error = libdecaf_decaf448:scalar_invert(libdecaf_decaf448:scalar_reduce(<<>>)).

scalar_is_canonical(Scalar)

-spec scalar_is_canonical(Scalar) -> Result when Scalar :: term(), Result :: boolean().

Return whether Scalar is a canonical 56-byte little-endian scalar.

Its integer value must be smaller than the group order; zero is accepted. Wrong-sized binaries, out-of-range values, and non-binaries return false.

scalar_mul(Left, Right)

-spec scalar_mul(Left, Right) -> Result when Left :: scalar(), Right :: scalar(), Result :: scalar().

Return (Left * Right) mod l, where l is the group order.

Both operands must already be canonical scalars; otherwise raises badarg. The result is canonically encoded.

scalar_negate(Scalar)

-spec scalar_negate(Scalar) -> Result when Scalar :: scalar(), Result :: scalar().

Return -Scalar mod l, where l is the group order.

The result adds to the input to give zero; zero is its own negation. A noncanonical scalar raises badarg.

scalar_random()

-spec scalar_random() -> Scalar when Scalar :: scalar().

Generate a canonical scalar using crypto:strong_rand_bytes/1.

Reduces 112 cryptographically random bytes modulo the group order. The result has negligible reduction bias and can be zero. Protocols requiring a nonzero scalar must retry when zero is returned. Random-source failures propagate from crypto.

Scalar = libdecaf_decaf448:scalar_random(),
true = libdecaf_decaf448:scalar_is_canonical(Scalar).

scalar_reduce(Bytes)

-spec scalar_reduce(Bytes) -> Result when Bytes :: binary(), Result :: scalar().

Reduce an arbitrary-length unsigned little-endian integer modulo the group order.

Returns a canonical 56-byte scalar. Leading zero bytes at the most-significant end are allowed, and the empty binary reduces to zero. This operation does not hash or clamp its input. Non-binaries raise badarg.

Scalar = libdecaf_decaf448:scalar_reduce(<<1, 2>>),
513 = binary:decode_unsigned(Scalar, little),
true = libdecaf_decaf448:scalar_is_canonical(Scalar).

scalar_sub(Left, Right)

-spec scalar_sub(Left, Right) -> Result when Left :: scalar(), Right :: scalar(), Result :: scalar().

Return (Left - Right) mod l, where l is the group order.

Both operands must already be canonical scalars; otherwise raises badarg. A negative difference wraps modulo l and the result is canonically encoded.