101std::uint64_t
ceil_div(std::uint64_t lhs, std::uint64_t rhs);
114 purify_sha256(tag_hash_.data(),
reinterpret_cast<const unsigned char*
>(tag.data()), tag.size());
117 [[nodiscard]] std::array<unsigned char, 32>
digest(std::span<const unsigned char> data)
const {
118 return digest_many<1>({data});
121 template <std::
size_t Segments>
122 [[nodiscard]] std::array<unsigned char, 32>
123 digest_many(
const std::array<std::span<const unsigned char>, Segments>& segments)
const {
124 std::array<unsigned char, 32> out{};
125 std::array<const unsigned char*, Segments + 2> items{};
126 std::array<size_t, Segments + 2> item_lens{};
127 items[0] = tag_hash_.data();
128 item_lens[0] = tag_hash_.size();
129 items[1] = tag_hash_.data();
130 item_lens[1] = tag_hash_.size();
131 for (std::size_t i = 0; i < Segments; ++i) {
132 items[i + 2] = segments[i].empty() ? nullptr : segments[i].data();
133 item_lens[i + 2] = segments[i].size();
135 int ok =
purify_sha256_many(out.data(), items.data(), item_lens.data(), items.size());
136 assert(ok != 0 &&
"TaggedHash::digest_many() must accept well-formed segments");
142 std::array<unsigned char, 32> tag_hash_{};
149template <std::
size_t Words>
152 for (
int i = 0; i < 256; ++i) {
153 Bytes salt{
static_cast<unsigned char>(i)};
154 Bytes derived =
hkdf((bits + 7) / 8, data, salt, info);
156 value.mask_bits(bits);
157 if (value.compare(range) < 0) {
165template <std::
size_t Words>
167 const TaggedHash& tag, std::span<const unsigned char> info = {}) {
169 std::size_t bytes_needed = (bits + 7) / 8;
170 for (std::uint64_t attempt = 0; attempt < 256; ++attempt) {
172 derived.reserve(bytes_needed);
173 std::array<unsigned char, 8> attempt_bytes{};
174 for (std::size_t i = 0; i < attempt_bytes.size(); ++i) {
175 attempt_bytes[attempt_bytes.size() - 1 - i] =
static_cast<unsigned char>((attempt >> (8 * i)) & 0xffU);
177 for (std::uint64_t block = 0; derived.size() < bytes_needed; ++block) {
178 std::array<unsigned char, 8> block_bytes{};
179 for (std::size_t i = 0; i < block_bytes.size(); ++i) {
180 block_bytes[block_bytes.size() - 1 - i] =
static_cast<unsigned char>((block >> (8 * i)) & 0xffU);
182 std::array digest_segments{
185 std::span<const unsigned char>(attempt_bytes.data(), attempt_bytes.size()),
186 std::span<const unsigned char>(block_bytes.data(), block_bytes.size()),
188 std::array<unsigned char, 32> digest = tag.
digest_many(digest_segments);
189 std::size_t copy_len = std::min<std::size_t>(digest.size(), bytes_needed - derived.size());
190 derived.insert(derived.end(), digest.begin(), digest.begin() +
static_cast<std::ptrdiff_t
>(copy_len));
193 value.mask_bits(bits);
194 if (value.compare(range) < 0) {
235const EllipticCurve&
curve1();
238const EllipticCurve&
curve2();
271FieldElement
combine(
const FieldElement& x1,
const FieldElement& x2);
Minimal elliptic-curve arithmetic over the Purify base field.
JacobianPoint mul(const JacobianPoint &point, const UInt256 &scalar) const
Multiplies a point by a scalar using double-and-add.
AffinePoint affine(const JacobianPoint &point) const
Converts a Jacobian point to affine coordinates.
JacobianPoint add_mixed(const JacobianPoint &lhs, const AffinePoint &rhs) const
Adds an affine point to a Jacobian point.
JacobianPoint negate(const JacobianPoint &point) const
Negates a point without changing its projective scale.
bool is_x_coord(const FieldElement &x) const
Returns true if the supplied x-coordinate lifts to a curve point.
std::optional< JacobianPoint > lift_x(const FieldElement &x) const
Lifts an x-coordinate to a Jacobian point when a square root exists.
Result< AffinePoint > mul_secret_affine(const JacobianPoint &point, const UInt256 &scalar) const
Multiplies a public point by a secret scalar using exception-free complete formulas.
JacobianPoint add(const JacobianPoint &lhs, const JacobianPoint &rhs) const
Adds two Jacobian points.
const UInt256 & order() const
Returns the subgroup order used for scalar multiplication checks.
JacobianPoint double_point(const JacobianPoint &point) const
Doubles a Jacobian point.
Purify result carrier that either holds a value or an error.
Field element modulo the backend scalar field used by this implementation.
Reusable BIP340-style tagged SHA-256 helper.
TaggedHash(std::string_view tag)
std::array< unsigned char, 32 > digest_many(const std::array< std::span< const unsigned char >, Segments > &segments) const
std::array< unsigned char, 32 > digest(std::span< const unsigned char > data) const
Bytes hmac_sha256(const Bytes &key, const Bytes &data)
Computes an HMAC-SHA256 digest using the secp bridge implementation.
Status validate_secret_key(const UInt512 &z)
Validates the packed secret-key encoding range.
FieldElement field_di()
Returns the inverse of the twist factor in the field.
const UInt256 & half_n1()
Returns floor(order_n1 / 2).
Result< std::vector< int > > key_to_bits(UInt256 n, const UInt256 &max_value)
Encodes a scalar into the signed 3-bit window bit schedule used by the circuit.
Result< std::pair< UInt256, UInt256 > > unpack_public(const UInt512 &packed)
Splits a packed public key into its two x-coordinates.
const UInt256 & half_n2()
Returns floor(order_n2 / 2).
BigUInt< 8 > UInt512
512-bit unsigned integer used for private and packed public keys.
Bytes bytes_from_ascii(std::string_view input)
Encodes an ASCII string as a byte vector.
std::uint64_t ceil_div(std::uint64_t lhs, std::uint64_t rhs)
Computes ceiling division for unsigned 64-bit values.
Bytes hkdf(std::size_t length, const Bytes &ikm, const Bytes &salt={}, const Bytes &info={})
Expands input key material using HKDF-SHA256.
const UInt256 & order_n2()
Returns the subgroup order for the second curve.
Status validate_public_key(const UInt512 &packed)
Validates the packed public-key encoding range.
bool is_valid_public_key(const UInt512 &packed)
Returns true when a packed public key is encoded canonically.
const UInt320 & two_p()
Returns 2 * prime_p() as a widened integer for hash-to-curve sampling.
FieldElement field_a()
Returns the shared Weierstrass a coefficient used by Purify.
const EllipticCurve & curve1()
Returns the first Purify curve instance.
std::vector< unsigned char > Bytes
Dynamically sized byte string used for messages, serialized witnesses, and proofs.
const JacobianPoint & generator1()
Returns the fixed generator for the first curve.
UInt512 pack_public(const UInt256 &x1, const UInt256 &x2)
Packs two x-coordinates into the reference 512-bit public-key encoding.
const UInt512 & packed_secret_key_space_size()
Returns the size of the packed secret-key encoding space.
Result< std::pair< UInt256, UInt256 > > unpack_secret(const UInt512 &z)
Splits a packed private key into its two per-curve secret scalars.
FieldElement field_d()
Returns the twist factor used to derive the second curve.
Result< JacobianPoint > hash_to_curve(const Bytes &data, const EllipticCurve &curve)
Hashes arbitrary data onto the supplied curve by rejection sampling x-coordinates.
BigUInt< 5 > UInt320
320-bit unsigned integer used during hash-to-curve sampling.
FieldElement field_b()
Returns the shared Weierstrass b coefficient used by Purify.
bool is_valid_secret_key(const UInt512 &z)
Returns true when a packed secret is encoded canonically.
const EllipticCurve & curve2()
Returns the second Purify curve instance.
const JacobianPoint & generator2()
Returns the fixed generator for the second curve.
const UInt256 & order_n1()
Returns the subgroup order for the first curve.
FieldElement combine(const FieldElement &x1, const FieldElement &x2)
Applies the Purify curve-combination map to two x-coordinates.
std::optional< BigUInt< Words > > tagged_hash_to_int(std::span< const unsigned char > data, const BigUInt< Words > &range, const TaggedHash &tag, std::span< const unsigned char > info={})
Rejection-samples a uniformly distributed integer below range using repeated tagged hashes.
const UInt512 & packed_public_key_space_size()
Returns the size of the packed public-key encoding space.
std::optional< BigUInt< Words > > hash_to_int(const Bytes &data, const BigUInt< Words > &range, const Bytes &info={})
Rejection-samples a uniformly distributed integer below range.
BigUInt< 4 > UInt256
256-bit unsigned integer used for field elements and curve orders.
Expected< void, Error > Status
Expected-returning convenience alias for Purify status-only APIs.
Bytes operator+(Bytes lhs, const Bytes &rhs)
Concatenates two byte vectors.
Fixed-width integer and field arithmetic helpers used throughout Purify.
int purify_sha256_many(unsigned char output32[32], const unsigned char *const *items, const size_t *item_lens, size_t items_count)
Computes SHA-256 over a set of byte strings.
void purify_sha256(unsigned char output32[32], const unsigned char *data, size_t data_len)
Computes SHA-256 over a byte string.
Affine point representation used for serialization and lookup tables.
std::size_t bit_length() const
Returns the index of the highest set bit plus one.
static BigUInt from_bytes_be(const unsigned char *data, std::size_t size)
Parses a big-endian byte string into the fixed-width integer.
Projective point used by the hardened secret-scalar multiplication path.
Jacobian point representation used for curve arithmetic.
static JacobianPoint infinity_point()
Returns the point at infinity in Jacobian coordinates.