purify
C++ Purify implementation with native circuit and BPP support
Loading...
Searching...
No Matches
legacy.cpp
Go to the documentation of this file.
1// Copyright (c) 2026 Judica, Inc.
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or https://opensource.org/license/mit/.
4
11
12#include <algorithm>
13#include <cstring>
14
16#include "detail/common.hpp"
17#include "purify/bppp.hpp"
18#include "purify/curve.hpp"
19
20namespace purify::puresign {
21
22namespace api_impl {
23
24Result<Signature> sign_message_with_prepared(const SecretKey& secret, std::span<const unsigned char> message,
25 PreparedNonce&& prepared, purify_secp_context* secp_context);
26Result<Signature> sign_with_prepared_topic(const SecretKey& secret, std::span<const unsigned char> message,
27 PreparedNonce&& prepared, purify_secp_context* secp_context);
28
29} // namespace api_impl
30
31namespace {
32
33constexpr std::string_view kMessageNonceTag = "PureSign/Nonce/Message/";
34constexpr std::string_view kTopicNonceTag = "PureSign/Nonce/Topic/";
35constexpr std::string_view kMessageBindingTag = "PureSign/Binding/Message";
36constexpr std::string_view kTopicBindingTag = "PureSign/Binding/Topic";
37constexpr std::string_view kMessageProofTag = "PureSign/Proof/Message/V1";
38constexpr std::string_view kTopicProofTag = "PureSign/Proof/Topic/V1";
39const TaggedHash kMessageBindingTaggedHash(kMessageBindingTag);
40const TaggedHash kTopicBindingTaggedHash(kTopicBindingTag);
41const TaggedHash kMessageProofTaggedHash(kMessageProofTag);
42const TaggedHash kTopicProofTaggedHash(kTopicProofTag);
43
44const TaggedHash& binding_tagged_hash(PreparedNonce::Scope scope) {
45 return scope == PreparedNonce::Scope::Message ? kMessageBindingTaggedHash : kTopicBindingTaggedHash;
46}
47
48const TaggedHash& proof_tagged_hash(PreparedNonce::Scope scope) {
49 return scope == PreparedNonce::Scope::Message ? kMessageProofTaggedHash : kTopicProofTaggedHash;
50}
51
52const UInt256& secp256k1_order() {
53 static const UInt256 value =
54 UInt256::from_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141");
55 return value;
56}
57
58Status validate_puresign_field_alignment() {
59 if (prime_p() != secp256k1_order()) {
60 return unexpected_error(ErrorCode::InternalMismatch, "puresign:field_order_mismatch");
61 }
62 return {};
63}
64
65Status validate_public_key_bundle(const PublicKey& public_key, purify_secp_context* secp_context) {
66 PURIFY_RETURN_IF_ERROR(validate_public_key(public_key.purify_pubkey),
67 "puresign:validate_public_key_bundle:purify_pubkey");
68 PURIFY_RETURN_IF_ERROR(require_secp_context(secp_context, "puresign:validate_public_key_bundle:secp_context"),
69 "puresign:validate_public_key_bundle:secp_context");
70 if (purify_bip340_validate_xonly_pubkey(secp_context, public_key.bip340_pubkey.data()) == 0) {
71 return unexpected_error(ErrorCode::BackendRejectedInput, "puresign:validate_public_key_bundle:bip340_pubkey");
72 }
73 return {};
74}
75
76const unsigned char* nullable_data(std::span<const unsigned char> input) {
77 return input.empty() ? nullptr : input.data();
78}
79
80XOnly32 binding_digest(const TaggedHash& tag, std::span<const unsigned char> input) {
81 XOnly32 out{};
82 out = tag.digest(input);
83 return out;
84}
85
86std::string_view proof_tag_for_scope(PreparedNonce::Scope scope) {
87 return scope == PreparedNonce::Scope::Message ? kMessageProofTag : kTopicProofTag;
88}
89
90Bytes proof_statement_binding(PreparedNonce::Scope scope) {
91 return bytes_from_ascii(proof_tag_for_scope(scope));
92}
93
94Scalar32 derive_proof_nonce_seed(const SecretKey& secret, PreparedNonce::Scope scope, std::span<const unsigned char> eval_input) {
95 std::array<unsigned char, 64> secret_bytes = secret.packed().to_bytes_be();
96 Scalar32 out{};
97 std::array digest_segments{
98 std::span<const unsigned char>(secret_bytes.data(), secret_bytes.size()),
100 };
101 out = proof_tagged_hash(scope).digest_many(digest_segments);
102 detail::secure_clear_bytes(secret_bytes.data(), secret_bytes.size());
103 return out;
104}
105
106Result<bool> nonce_proof_matches_nonce(const NonceProof& nonce_proof, purify_secp_context* secp_context) {
107 XOnly32 xonly{};
108 int parity = 0;
109 PURIFY_RETURN_IF_ERROR(require_secp_context(secp_context, "nonce_proof_matches_nonce:secp_context"),
110 "nonce_proof_matches_nonce:secp_context");
111 if (purify_bip340_xonly_from_point(secp_context, nonce_proof.proof.commitment.data(), xonly.data(), &parity) == 0) {
112 return unexpected_error(ErrorCode::BackendRejectedInput, "nonce_proof_matches_nonce:invalid_commitment");
113 }
114 (void)parity;
115 return xonly == nonce_proof.nonce.xonly;
116}
117
118Status validate_message_proof_cache(const MessageProofCache& cache) {
119 return detail::validate_message_proof_cache(cache, kMessageNonceTag);
120}
121
122Status validate_topic_proof_cache(const TopicProofCache& cache) {
123 return detail::validate_topic_proof_cache(cache, kTopicNonceTag);
124}
125
126struct DerivedNonceData {
129 Nonce nonce{};
133};
134
135Result<NonceProof> build_nonce_proof_from_template(const SecretKey& secret, const DerivedNonceData& nonce_data,
136 const NativeBulletproofCircuitTemplate& circuit_template,
137 purify_secp_context* secp_context,
138 ExperimentalBulletproofBackendCache* backend_cache = nullptr) {
139 PURIFY_ASSIGN_OR_RETURN(const auto& witness, prove_assignment_data(nonce_data.eval_input, secret),
140 "build_nonce_proof_from_template:prove_assignment_data");
141 PURIFY_ASSIGN_OR_RETURN(auto partial_ok, circuit_template.partial_evaluate(witness.assignment),
142 "build_nonce_proof_from_template:partial_evaluate");
143 if (!partial_ok) {
144 return unexpected_error(ErrorCode::BindingMismatch, "build_nonce_proof_from_template:partial_evaluate_false");
145 }
146 PURIFY_ASSIGN_OR_RETURN(auto final_ok, circuit_template.final_evaluate(witness.assignment, witness.public_key),
147 "build_nonce_proof_from_template:final_evaluate");
148 if (!final_ok) {
149 return unexpected_error(ErrorCode::BindingMismatch, "build_nonce_proof_from_template:final_evaluate_false");
150 }
151 PURIFY_ASSIGN_OR_RETURN(const auto& circuit, circuit_template.instantiate_packed(witness.public_key),
152 "build_nonce_proof_from_template:instantiate_packed");
153 PURIFY_RETURN_IF_ERROR(detail::validate_proof_cache_circuit(circuit, "build_nonce_proof_from_template:circuit_shape"),
154 "build_nonce_proof_from_template:validate_proof_cache_circuit");
155
156 Scalar32 proof_nonce = derive_proof_nonce_seed(secret, nonce_data.scope, nonce_data.eval_input);
157 Bytes statement_binding = proof_statement_binding(nonce_data.scope);
159 auto proof,
160 prove_experimental_circuit_assume_valid(circuit, witness.assignment, proof_nonce,
161 bppp::base_generator(secp_context), secp_context,
162 statement_binding, std::nullopt,
163 backend_cache),
164 "build_nonce_proof_from_template:prove_experimental_circuit");
165
166 NonceProof out{};
167 out.nonce = nonce_data.nonce;
168 out.proof = std::move(proof);
169 PURIFY_ASSIGN_OR_RETURN(auto match, nonce_proof_matches_nonce(out, secp_context),
170 "build_nonce_proof_from_template:nonce_proof_matches_nonce");
171 if (!match) {
172 return unexpected_error(ErrorCode::InternalMismatch, "build_nonce_proof_from_template:nonce_mismatch");
173 }
174 return out;
175}
176
177Result<DerivedNonceData> derive_nonce_data(const SecretKey& secret, PreparedNonce::Scope scope,
178 std::span<const unsigned char> input,
179 purify_secp_context* secp_context) {
180 PURIFY_RETURN_IF_ERROR(validate_puresign_field_alignment(), "derive_nonce_data:validate_puresign_field_alignment");
181 PURIFY_ASSIGN_OR_RETURN(const auto& signer, derive_bip340_key(secret, secp_context), "derive_nonce_data:derive_bip340_key");
182
183 const std::string_view nonce_tag = scope == PreparedNonce::Scope::Message ? kMessageNonceTag : kTopicNonceTag;
184 const TaggedHash& binding_hash = binding_tagged_hash(scope);
185
186 DerivedNonceData out{};
187 out.scope = scope;
188 out.signer_pubkey = signer.xonly_pubkey;
189 out.binding_digest = binding_digest(binding_hash, input);
190
191 out.eval_input = detail::tagged_eval_input(nonce_tag, input);
192 PURIFY_ASSIGN_OR_RETURN(const auto& nonce_value, eval(secret, out.eval_input), "derive_nonce_data:eval");
193 out.scalar = nonce_value.to_bytes_be();
194 if (std::all_of(out.scalar.begin(), out.scalar.end(), [](unsigned char byte) { return byte == 0; })) {
195 /* This rejects the unique invalid Schnorr nonce. Since Purify outputs a field element
196 * modulo the secp256k1 group order, the failure probability is exactly 1/n, which is
197 * negligible for n ~= 2^256.
198 */
199 return unexpected_error(ErrorCode::BackendRejectedInput, "derive_nonce_data:zero_nonce");
200 }
201
202 PURIFY_RETURN_IF_ERROR(require_secp_context(secp_context, "derive_nonce_data:secp_context"),
203 "derive_nonce_data:secp_context");
204 if (purify_bip340_nonce_from_scalar(secp_context, out.scalar.data(), out.nonce.xonly.data()) == 0) {
205 return unexpected_error(ErrorCode::BackendRejectedInput, "derive_nonce_data:bip340_nonce_from_scalar");
206 }
207 return out;
208}
209
210Result<NonceProof> build_nonce_proof(const SecretKey& secret,
211 const DerivedNonceData& nonce_data,
212 purify_secp_context* secp_context) {
213 PURIFY_ASSIGN_OR_RETURN(const auto& circuit_template, verifier_circuit_template(nonce_data.eval_input),
214 "build_nonce_proof:verifier_circuit_template");
215 return build_nonce_proof_from_template(secret, nonce_data, circuit_template, secp_context);
216}
217
218Result<DerivedNonceData> prepare_nonce_data_impl(const SecretKey& secret, PreparedNonce::Scope scope,
219 std::span<const unsigned char> input,
220 purify_secp_context* secp_context) {
221 if (scope == PreparedNonce::Scope::Topic && input.empty()) {
222 return unexpected_error(ErrorCode::EmptyInput, "prepare_nonce_data_impl:empty_topic");
223 }
224 PURIFY_ASSIGN_OR_RETURN(auto nonce_data, derive_nonce_data(secret, scope, input, secp_context),
225 "prepare_nonce_data_impl:derive_nonce_data");
226 return nonce_data;
227}
228
229Result<bool> verify_nonce_proof_with_circuit(const PublicKey& public_key, const NativeBulletproofCircuit& circuit,
230 const NonceProof& nonce_proof, PreparedNonce::Scope scope,
231 purify_secp_context* secp_context,
232 const char* context,
233 ExperimentalBulletproofBackendCache* backend_cache = nullptr) {
234 PURIFY_RETURN_IF_ERROR(validate_public_key_bundle(public_key, secp_context), context);
236 PURIFY_ASSIGN_OR_RETURN(auto match, nonce_proof_matches_nonce(nonce_proof, secp_context), context);
237 if (!match) {
238 return false;
239 }
240 Bytes statement_binding = proof_statement_binding(scope);
241 return verify_experimental_circuit(circuit, nonce_proof.proof, bppp::base_generator(secp_context), secp_context, statement_binding,
242 backend_cache);
243}
244
245Result<bool> verify_nonce_proof_with_circuit(const PublicKey& public_key,
246 const NativeBulletproofCircuit::PackedWithSlack& circuit,
247 const NonceProof& nonce_proof, PreparedNonce::Scope scope,
248 purify_secp_context* secp_context,
249 const char* context,
250 ExperimentalBulletproofBackendCache* backend_cache = nullptr) {
251 PURIFY_RETURN_IF_ERROR(validate_public_key_bundle(public_key, secp_context), context);
253 PURIFY_ASSIGN_OR_RETURN(auto match, nonce_proof_matches_nonce(nonce_proof, secp_context), context);
254 if (!match) {
255 return false;
256 }
257 Bytes statement_binding = proof_statement_binding(scope);
258 return verify_experimental_circuit(circuit, nonce_proof.proof, bppp::base_generator(secp_context), secp_context, statement_binding,
259 backend_cache);
260}
261
262} // namespace
263
264PreparedNonce::PreparedNonce(Scope scope, const Scalar32& scalar, const Nonce& nonce,
266 : scope_(scope), scalar_(scalar), nonce_(nonce), signer_pubkey_(signer_pubkey), binding_digest_(binding_digest) {}
267
268PreparedNonce PreparedNonce::from_parts(Scope scope, const Scalar32& scalar, const Nonce& nonce,
271}
272
273PreparedNonce::PreparedNonce(PreparedNonce&& other) noexcept
274 : scope_(other.scope_), scalar_(other.scalar_), nonce_(other.nonce_),
275 signer_pubkey_(other.signer_pubkey_), binding_digest_(other.binding_digest_) {
276 other.clear();
277}
278
279PreparedNonce& PreparedNonce::operator=(PreparedNonce&& other) noexcept {
280 if (this != &other) {
281 clear();
282 scope_ = other.scope_;
283 scalar_ = other.scalar_;
284 nonce_ = other.nonce_;
285 signer_pubkey_ = other.signer_pubkey_;
286 binding_digest_ = other.binding_digest_;
287 other.clear();
288 }
289 return *this;
290}
291
292PreparedNonce::~PreparedNonce() {
293 clear();
294}
295
296void PreparedNonce::clear() noexcept {
297 std::fill(scalar_.begin(), scalar_.end(), static_cast<unsigned char>(0));
298}
299
300Result<Signature> PreparedNonce::sign_message(const Bip340Key& signer,
301 std::span<const unsigned char> message,
302 purify_secp_context* secp_context) && {
303 if (scope_ != Scope::Message) {
304 return unexpected_error(ErrorCode::BindingMismatch, "PreparedNonce::sign_message:scope");
305 }
306 if (signer_pubkey_ != signer.xonly_pubkey) {
307 return unexpected_error(ErrorCode::BindingMismatch, "PreparedNonce::sign_message:signer_pubkey");
308 }
309 if (binding_digest_ != binding_digest(kMessageBindingTaggedHash, message)) {
310 return unexpected_error(ErrorCode::BindingMismatch, "PreparedNonce::sign_message:message_binding");
311 }
312
313 PURIFY_RETURN_IF_ERROR(require_secp_context(secp_context, "PreparedNonce::sign_message:secp_context"),
314 "PreparedNonce::sign_message:secp_context");
315 Signature out{};
316 if (purify_bip340_sign_with_fixed_nonce(secp_context, out.bytes.data(), nullable_data(message), message.size(),
317 signer.seckey.data(), scalar_.data()) == 0) {
318 return unexpected_error(ErrorCode::BackendRejectedInput, "PreparedNonce::sign_message:sign_with_fixed_nonce");
319 }
320 if (out.nonce().xonly != nonce_.xonly) {
321 return unexpected_error(ErrorCode::InternalMismatch, "PreparedNonce::sign_message:nonce_mismatch");
322 }
323 if (purify_bip340_verify(secp_context, out.bytes.data(), nullable_data(message), message.size(),
324 signer.xonly_pubkey.data()) == 0) {
325 return unexpected_error(ErrorCode::InternalMismatch, "PreparedNonce::sign_message:self_verify");
326 }
327 return out;
328}
329
330Result<Signature> PreparedNonce::sign_topic_message(const Bip340Key& signer,
331 std::span<const unsigned char> message,
332 purify_secp_context* secp_context) && {
333 if (scope_ != Scope::Topic) {
334 return unexpected_error(ErrorCode::BindingMismatch, "PreparedNonce::sign_topic_message:scope");
335 }
336 if (signer_pubkey_ != signer.xonly_pubkey) {
337 return unexpected_error(ErrorCode::BindingMismatch, "PreparedNonce::sign_topic_message:signer_pubkey");
338 }
339
340 PURIFY_RETURN_IF_ERROR(require_secp_context(secp_context, "PreparedNonce::sign_topic_message:secp_context"),
341 "PreparedNonce::sign_topic_message:secp_context");
342 Signature out{};
343 if (purify_bip340_sign_with_fixed_nonce(secp_context, out.bytes.data(), nullable_data(message), message.size(),
344 signer.seckey.data(), scalar_.data()) == 0) {
345 return unexpected_error(ErrorCode::BackendRejectedInput,
346 "PreparedNonce::sign_topic_message:sign_with_fixed_nonce");
347 }
348 if (out.nonce().xonly != nonce_.xonly) {
349 return unexpected_error(ErrorCode::InternalMismatch, "PreparedNonce::sign_topic_message:nonce_mismatch");
350 }
351 if (purify_bip340_verify(secp_context, out.bytes.data(), nullable_data(message), message.size(),
352 signer.xonly_pubkey.data()) == 0) {
353 return unexpected_error(ErrorCode::InternalMismatch, "PreparedNonce::sign_topic_message:self_verify");
354 }
355 return out;
356}
357
358PreparedNonceWithProof PreparedNonceWithProof::from_parts(PreparedNonce prepared, NonceProof proof) {
359 return PreparedNonceWithProof(std::move(prepared), std::move(proof));
360}
361
362Result<ProvenSignature> PreparedNonceWithProof::sign_message(const SecretKey& secret,
363 std::span<const unsigned char> message,
364 purify_secp_context* secp_context) && {
365 NonceProof nonce_proof = std::move(proof_);
366 PURIFY_ASSIGN_OR_RETURN(auto signature,
367 api_impl::sign_message_with_prepared(secret, message, std::move(prepared_), secp_context),
368 "PreparedNonceWithProof::sign_message:sign_message_with_prepared");
369 if (signature.nonce().xonly != nonce_proof.nonce.xonly) {
370 return unexpected_error(ErrorCode::InternalMismatch, "PreparedNonceWithProof::sign_message:nonce_mismatch");
371 }
372 return ProvenSignature{signature, std::move(nonce_proof)};
373}
374
375Result<ProvenSignature> PreparedNonceWithProof::sign_topic_message(const SecretKey& secret,
376 std::span<const unsigned char> message,
377 purify_secp_context* secp_context) && {
378 NonceProof nonce_proof = std::move(proof_);
379 PURIFY_ASSIGN_OR_RETURN(auto signature,
380 api_impl::sign_with_prepared_topic(secret, message, std::move(prepared_), secp_context),
381 "PreparedNonceWithProof::sign_topic_message:sign_with_prepared_topic");
382 if (signature.nonce().xonly != nonce_proof.nonce.xonly) {
383 return unexpected_error(ErrorCode::InternalMismatch, "PreparedNonceWithProof::sign_topic_message:nonce_mismatch");
384 }
385 return ProvenSignature{signature, std::move(nonce_proof)};
386}
387
388namespace api_impl {
389
390Result<PublicKey> derive_public_key(const SecretKey& secret, purify_secp_context* secp_context) {
391 PURIFY_ASSIGN_OR_RETURN(const auto& purify_key, derive_key(secret), "derive_public_key:derive_key");
392 PURIFY_ASSIGN_OR_RETURN(const auto& bip340_key, derive_bip340_key(secret, secp_context), "derive_public_key:derive_bip340_key");
393 return PublicKey{purify_key.public_key, bip340_key.xonly_pubkey};
394}
395
396Result<MessageProofCache> build_message_proof_cache(std::span<const unsigned char> message) {
397 Bytes eval_input = detail::tagged_eval_input(kMessageNonceTag, message);
399 "build_message_proof_cache:verifier_circuit_template");
400 PURIFY_ASSIGN_OR_RETURN(auto template_digest, circuit_template.integrity_digest(),
401 "build_message_proof_cache:integrity_digest");
402 MessageProofCache cache{};
403 cache.message = detail::copy_bytes(message);
404 cache.eval_input = std::move(eval_input);
405 cache.circuit_template = std::move(circuit_template);
406 cache.template_digest = std::move(template_digest);
407 return cache;
408}
409
410Result<TopicProofCache> build_topic_proof_cache(std::span<const unsigned char> topic) {
411 if (topic.empty()) {
412 return unexpected_error(ErrorCode::EmptyInput, "build_topic_proof_cache:empty_topic");
413 }
414 Bytes eval_input = detail::tagged_eval_input(kTopicNonceTag, topic);
416 "build_topic_proof_cache:verifier_circuit_template");
417 PURIFY_ASSIGN_OR_RETURN(auto template_digest, circuit_template.integrity_digest(),
418 "build_topic_proof_cache:integrity_digest");
419 TopicProofCache cache{};
420 cache.topic = detail::copy_bytes(topic);
421 cache.eval_input = std::move(eval_input);
422 cache.circuit_template = std::move(circuit_template);
423 cache.template_digest = std::move(template_digest);
424 return cache;
425}
426
427Result<PreparedNonce> prepare_message_nonce(const SecretKey& secret, std::span<const unsigned char> message,
428 purify_secp_context* secp_context) {
429 PURIFY_ASSIGN_OR_RETURN(const auto& nonce_data,
430 prepare_nonce_data_impl(secret, PreparedNonce::Scope::Message, message, secp_context),
431 "prepare_message_nonce:prepare_nonce_data_impl");
432 return PreparedNonce::from_parts(PreparedNonce::Scope::Message, nonce_data.scalar, nonce_data.nonce,
433 nonce_data.signer_pubkey, nonce_data.binding_digest);
434}
435
436Result<PreparedNonceWithProof> prepare_message_nonce_with_proof(const SecretKey& secret,
437 std::span<const unsigned char> message,
438 purify_secp_context* secp_context) {
439 PURIFY_ASSIGN_OR_RETURN(const auto& nonce_data,
440 prepare_nonce_data_impl(secret, PreparedNonce::Scope::Message, message, secp_context),
441 "prepare_message_nonce_with_proof:prepare_nonce_data_impl");
442 PreparedNonce prepared = PreparedNonce::from_parts(PreparedNonce::Scope::Message, nonce_data.scalar,
443 nonce_data.nonce, nonce_data.signer_pubkey,
444 nonce_data.binding_digest);
445 PURIFY_ASSIGN_OR_RETURN(auto proof, build_nonce_proof(secret, nonce_data, secp_context),
446 "prepare_message_nonce_with_proof:build_nonce_proof");
447 return PreparedNonceWithProof::from_parts(std::move(prepared), std::move(proof));
448}
449
450Result<PreparedNonceWithProof> prepare_message_nonce_with_proof(const SecretKey& secret,
451 const MessageProofCache& cache,
452 purify_secp_context* secp_context) {
453 PURIFY_RETURN_IF_ERROR(validate_message_proof_cache(cache),
454 "prepare_message_nonce_with_proof:validate_message_proof_cache");
455 PURIFY_ASSIGN_OR_RETURN(const auto& nonce_data,
456 prepare_nonce_data_impl(secret, PreparedNonce::Scope::Message, cache.message, secp_context),
457 "prepare_message_nonce_with_proof:prepare_nonce_data_impl");
458 PreparedNonce prepared = PreparedNonce::from_parts(PreparedNonce::Scope::Message, nonce_data.scalar,
459 nonce_data.nonce, nonce_data.signer_pubkey,
460 nonce_data.binding_digest);
461 PURIFY_ASSIGN_OR_RETURN(auto proof,
462 build_nonce_proof_from_template(secret, nonce_data, cache.circuit_template, secp_context,
463 &cache.backend_cache),
464 "prepare_message_nonce_with_proof:build_nonce_proof_from_template");
465 return PreparedNonceWithProof::from_parts(std::move(prepared), std::move(proof));
466}
467
468Result<PreparedNonce> prepare_topic_nonce(const SecretKey& secret, std::span<const unsigned char> topic,
469 purify_secp_context* secp_context) {
470 PURIFY_ASSIGN_OR_RETURN(const auto& nonce_data,
471 prepare_nonce_data_impl(secret, PreparedNonce::Scope::Topic, topic, secp_context),
472 "prepare_topic_nonce:prepare_nonce_data_impl");
473 return PreparedNonce::from_parts(PreparedNonce::Scope::Topic, nonce_data.scalar, nonce_data.nonce,
474 nonce_data.signer_pubkey, nonce_data.binding_digest);
475}
476
477Result<PreparedNonceWithProof> prepare_topic_nonce_with_proof(const SecretKey& secret,
478 std::span<const unsigned char> topic,
479 purify_secp_context* secp_context) {
480 PURIFY_ASSIGN_OR_RETURN(const auto& nonce_data,
481 prepare_nonce_data_impl(secret, PreparedNonce::Scope::Topic, topic, secp_context),
482 "prepare_topic_nonce_with_proof:prepare_nonce_data_impl");
483 PreparedNonce prepared = PreparedNonce::from_parts(PreparedNonce::Scope::Topic, nonce_data.scalar,
484 nonce_data.nonce, nonce_data.signer_pubkey,
485 nonce_data.binding_digest);
486 PURIFY_ASSIGN_OR_RETURN(auto proof, build_nonce_proof(secret, nonce_data, secp_context),
487 "prepare_topic_nonce_with_proof:build_nonce_proof");
488 return PreparedNonceWithProof::from_parts(std::move(prepared), std::move(proof));
489}
490
491Result<PreparedNonceWithProof> prepare_topic_nonce_with_proof(const SecretKey& secret,
492 const TopicProofCache& cache,
493 purify_secp_context* secp_context) {
494 PURIFY_RETURN_IF_ERROR(validate_topic_proof_cache(cache),
495 "prepare_topic_nonce_with_proof:validate_topic_proof_cache");
496 PURIFY_ASSIGN_OR_RETURN(const auto& nonce_data,
497 prepare_nonce_data_impl(secret, PreparedNonce::Scope::Topic, cache.topic, secp_context),
498 "prepare_topic_nonce_with_proof:prepare_nonce_data_impl");
499 PreparedNonce prepared = PreparedNonce::from_parts(PreparedNonce::Scope::Topic, nonce_data.scalar,
500 nonce_data.nonce, nonce_data.signer_pubkey,
501 nonce_data.binding_digest);
502 PURIFY_ASSIGN_OR_RETURN(auto proof,
503 build_nonce_proof_from_template(secret, nonce_data, cache.circuit_template, secp_context,
504 &cache.backend_cache),
505 "prepare_topic_nonce_with_proof:build_nonce_proof_from_template");
506 return PreparedNonceWithProof::from_parts(std::move(prepared), std::move(proof));
507}
508
509Result<Signature> sign_message(const SecretKey& secret, std::span<const unsigned char> message,
510 purify_secp_context* secp_context) {
511 PURIFY_ASSIGN_OR_RETURN(auto prepared, prepare_message_nonce(secret, message, secp_context), "sign_message:prepare_message_nonce");
512 return sign_message_with_prepared(secret, message, std::move(prepared), secp_context);
513}
514
515Result<Signature> sign_message_with_prepared(const SecretKey& secret, std::span<const unsigned char> message,
516 PreparedNonce&& prepared, purify_secp_context* secp_context) {
517 PURIFY_ASSIGN_OR_RETURN(const auto& signer, derive_bip340_key(secret, secp_context), "sign_message_with_prepared:derive_bip340_key");
518 return std::move(prepared).sign_message(signer, message, secp_context);
519}
520
521Result<ProvenSignature> sign_message_with_prepared_proof(const SecretKey& secret, std::span<const unsigned char> message,
522 PreparedNonceWithProof&& prepared,
523 purify_secp_context* secp_context) {
524 return std::move(prepared).sign_message(secret, message, secp_context);
525}
526
527Result<Signature> sign_with_topic(const SecretKey& secret, std::span<const unsigned char> message,
528 std::span<const unsigned char> topic,
529 purify_secp_context* secp_context) {
530 PURIFY_ASSIGN_OR_RETURN(auto prepared, prepare_topic_nonce(secret, topic, secp_context), "sign_with_topic:prepare_topic_nonce");
531 return sign_with_prepared_topic(secret, message, std::move(prepared), secp_context);
532}
533
534Result<Signature> sign_with_prepared_topic(const SecretKey& secret, std::span<const unsigned char> message,
535 PreparedNonce&& prepared, purify_secp_context* secp_context) {
536 PURIFY_ASSIGN_OR_RETURN(const auto& signer, derive_bip340_key(secret, secp_context), "sign_with_prepared_topic:derive_bip340_key");
537 return std::move(prepared).sign_topic_message(signer, message, secp_context);
538}
539
540Result<ProvenSignature> sign_with_prepared_topic_proof(const SecretKey& secret, std::span<const unsigned char> message,
541 PreparedNonceWithProof&& prepared,
542 purify_secp_context* secp_context) {
543 return std::move(prepared).sign_topic_message(secret, message, secp_context);
544}
545
546Result<ProvenSignature> sign_message_with_proof(const SecretKey& secret, std::span<const unsigned char> message,
547 purify_secp_context* secp_context) {
548 PURIFY_ASSIGN_OR_RETURN(auto prepared, prepare_message_nonce_with_proof(secret, message, secp_context),
549 "sign_message_with_proof:prepare_message_nonce_with_proof");
550 return sign_message_with_prepared_proof(secret, message, std::move(prepared), secp_context);
551}
552
553Result<ProvenSignature> sign_message_with_proof(const SecretKey& secret, const MessageProofCache& cache,
554 purify_secp_context* secp_context) {
555 PURIFY_ASSIGN_OR_RETURN(auto prepared, prepare_message_nonce_with_proof(secret, cache, secp_context),
556 "sign_message_with_proof:prepare_message_nonce_with_proof_cache");
557 return sign_message_with_prepared_proof(secret, cache.message, std::move(prepared), secp_context);
558}
559
560Result<ProvenSignature> sign_with_topic_proof(const SecretKey& secret, std::span<const unsigned char> message,
561 std::span<const unsigned char> topic,
562 purify_secp_context* secp_context) {
563 PURIFY_ASSIGN_OR_RETURN(auto prepared, prepare_topic_nonce_with_proof(secret, topic, secp_context),
564 "sign_with_topic_proof:prepare_topic_nonce_with_proof");
565 return sign_with_prepared_topic_proof(secret, message, std::move(prepared), secp_context);
566}
567
568Result<ProvenSignature> sign_with_topic_proof(const SecretKey& secret, std::span<const unsigned char> message,
569 const TopicProofCache& cache,
570 purify_secp_context* secp_context) {
571 PURIFY_ASSIGN_OR_RETURN(auto prepared, prepare_topic_nonce_with_proof(secret, cache, secp_context),
572 "sign_with_topic_proof:prepare_topic_nonce_with_proof_cache");
573 return sign_with_prepared_topic_proof(secret, message, std::move(prepared), secp_context);
574}
575
576Result<bool> verify_signature(const PublicKey& public_key, std::span<const unsigned char> message,
577 const Signature& signature, purify_secp_context* secp_context) {
578 PURIFY_RETURN_IF_ERROR(validate_public_key(public_key.purify_pubkey), "verify_signature:validate_public_key");
579 PURIFY_RETURN_IF_ERROR(require_secp_context(secp_context, "verify_signature:secp_context"),
580 "verify_signature:secp_context");
581 if (purify_bip340_validate_xonly_pubkey(secp_context, public_key.bip340_pubkey.data()) == 0) {
582 return unexpected_error(ErrorCode::BackendRejectedInput, "verify_signature:bip340_validate_xonly_pubkey");
583 }
584 if (purify_bip340_validate_signature(secp_context, signature.bytes.data()) == 0) {
585 return unexpected_error(ErrorCode::BackendRejectedInput, "verify_signature:bip340_validate_signature");
586 }
587 return purify_bip340_verify(secp_context, signature.bytes.data(), nullable_data(message), message.size(),
588 public_key.bip340_pubkey.data()) != 0;
589}
590
591Result<bool> verify_message_nonce_proof(const PublicKey& public_key, std::span<const unsigned char> message,
592 const NonceProof& nonce_proof,
593 purify_secp_context* secp_context,
595 PURIFY_ASSIGN_OR_RETURN(const auto& circuit,
596 verifier_circuit(detail::tagged_eval_input(kMessageNonceTag, message), public_key.purify_pubkey),
597 "verify_message_nonce_proof:verifier_circuit");
598 return verify_nonce_proof_with_circuit(public_key, circuit, nonce_proof, PreparedNonce::Scope::Message, secp_context,
599 "verify_message_nonce_proof:verify_nonce_proof_with_circuit",
600 circuit_cache);
601}
602
603Result<bool> verify_message_nonce_proof(const MessageProofCache& cache, const PublicKey& public_key,
604 const NonceProof& nonce_proof,
605 purify_secp_context* secp_context,
607 PURIFY_RETURN_IF_ERROR(validate_message_proof_cache(cache), "verify_message_nonce_proof:validate_message_proof_cache");
608 PURIFY_ASSIGN_OR_RETURN(const auto& circuit, cache.circuit_template.instantiate_packed(public_key.purify_pubkey),
609 "verify_message_nonce_proof:instantiate_packed");
610 return verify_nonce_proof_with_circuit(public_key, circuit, nonce_proof, PreparedNonce::Scope::Message, secp_context,
611 "verify_message_nonce_proof:verify_nonce_proof_with_circuit",
612 circuit_cache != nullptr ? circuit_cache : &cache.backend_cache);
613}
614
615Result<bool> verify_topic_nonce_proof(const PublicKey& public_key, std::span<const unsigned char> topic,
616 const NonceProof& nonce_proof,
617 purify_secp_context* secp_context,
619 if (topic.empty()) {
620 return unexpected_error(ErrorCode::EmptyInput, "verify_topic_nonce_proof:empty_topic");
621 }
622 PURIFY_ASSIGN_OR_RETURN(const auto& circuit,
623 verifier_circuit(detail::tagged_eval_input(kTopicNonceTag, topic), public_key.purify_pubkey),
624 "verify_topic_nonce_proof:verifier_circuit");
625 return verify_nonce_proof_with_circuit(public_key, circuit, nonce_proof, PreparedNonce::Scope::Topic, secp_context,
626 "verify_topic_nonce_proof:verify_nonce_proof_with_circuit",
627 circuit_cache);
628}
629
630Result<bool> verify_topic_nonce_proof(const TopicProofCache& cache, const PublicKey& public_key,
631 const NonceProof& nonce_proof,
632 purify_secp_context* secp_context,
634 PURIFY_RETURN_IF_ERROR(validate_topic_proof_cache(cache), "verify_topic_nonce_proof:validate_topic_proof_cache");
635 PURIFY_ASSIGN_OR_RETURN(const auto& circuit, cache.circuit_template.instantiate_packed(public_key.purify_pubkey),
636 "verify_topic_nonce_proof:instantiate_packed");
637 return verify_nonce_proof_with_circuit(public_key, circuit, nonce_proof, PreparedNonce::Scope::Topic, secp_context,
638 "verify_topic_nonce_proof:verify_nonce_proof_with_circuit",
639 circuit_cache != nullptr ? circuit_cache : &cache.backend_cache);
640}
641
642Result<bool> verify_message_signature_with_proof(const PublicKey& public_key, std::span<const unsigned char> message,
643 const ProvenSignature& signature,
644 purify_secp_context* secp_context,
646 PURIFY_ASSIGN_OR_RETURN(auto sig_ok, verify_signature(public_key, message, signature.signature, secp_context),
647 "verify_message_signature_with_proof:verify_signature");
648 if (!sig_ok) {
649 return false;
650 }
651 if (signature.signature.nonce().xonly != signature.nonce_proof.nonce.xonly) {
652 return false;
653 }
654 return verify_message_nonce_proof(public_key, message, signature.nonce_proof, secp_context, circuit_cache);
655}
656
657Result<bool> verify_message_signature_with_proof(const MessageProofCache& cache, const PublicKey& public_key,
658 const ProvenSignature& signature,
659 purify_secp_context* secp_context,
661 PURIFY_ASSIGN_OR_RETURN(auto sig_ok, verify_signature(public_key, cache.message, signature.signature, secp_context),
662 "verify_message_signature_with_proof:verify_signature");
663 if (!sig_ok) {
664 return false;
665 }
666 if (signature.signature.nonce().xonly != signature.nonce_proof.nonce.xonly) {
667 return false;
668 }
669 return verify_message_nonce_proof(cache, public_key, signature.nonce_proof, secp_context, circuit_cache);
670}
671
672Result<bool> verify_topic_signature_with_proof(const PublicKey& public_key, std::span<const unsigned char> message,
673 std::span<const unsigned char> topic,
674 const ProvenSignature& signature,
675 purify_secp_context* secp_context,
677 PURIFY_ASSIGN_OR_RETURN(auto sig_ok, verify_signature(public_key, message, signature.signature, secp_context),
678 "verify_topic_signature_with_proof:verify_signature");
679 if (!sig_ok) {
680 return false;
681 }
682 if (signature.signature.nonce().xonly != signature.nonce_proof.nonce.xonly) {
683 return false;
684 }
685 return verify_topic_nonce_proof(public_key, topic, signature.nonce_proof, secp_context, circuit_cache);
686}
687
688Result<bool> verify_topic_signature_with_proof(const TopicProofCache& cache, const PublicKey& public_key,
689 std::span<const unsigned char> message,
690 const ProvenSignature& signature,
691 purify_secp_context* secp_context,
693 PURIFY_ASSIGN_OR_RETURN(auto sig_ok, verify_signature(public_key, message, signature.signature, secp_context),
694 "verify_topic_signature_with_proof:verify_signature");
695 if (!sig_ok) {
696 return false;
697 }
698 if (signature.signature.nonce().xonly != signature.nonce_proof.nonce.xonly) {
699 return false;
700 }
701 return verify_topic_nonce_proof(cache, public_key, signature.nonce_proof, secp_context, circuit_cache);
702}
703
704} // namespace api_impl
705
706Result<PublicKey> PublicKey::from_secret(const SecretKey& secret, purify_secp_context* secp_context) {
707 return api_impl::derive_public_key(secret, secp_context);
708}
709
710Result<bool> PublicKey::verify_signature(std::span<const unsigned char> message, const Signature& signature,
711 purify_secp_context* secp_context) const {
712 return api_impl::verify_signature(*this, message, signature, secp_context);
713}
714
715Result<bool> PublicKey::verify_message_nonce_proof(std::span<const unsigned char> message,
716 const NonceProof& nonce_proof,
717 purify_secp_context* secp_context,
718 ExperimentalBulletproofBackendCache* circuit_cache) const {
719 return api_impl::verify_message_nonce_proof(*this, message, nonce_proof, secp_context, circuit_cache);
720}
721
722Result<bool> PublicKey::verify_message_nonce_proof(const MessageProofCache& cache, const NonceProof& nonce_proof,
723 purify_secp_context* secp_context,
724 ExperimentalBulletproofBackendCache* circuit_cache) const {
725 return api_impl::verify_message_nonce_proof(cache, *this, nonce_proof, secp_context, circuit_cache);
726}
727
728Result<bool> PublicKey::verify_topic_nonce_proof(std::span<const unsigned char> topic,
729 const NonceProof& nonce_proof,
730 purify_secp_context* secp_context,
731 ExperimentalBulletproofBackendCache* circuit_cache) const {
732 return api_impl::verify_topic_nonce_proof(*this, topic, nonce_proof, secp_context, circuit_cache);
733}
734
735Result<bool> PublicKey::verify_topic_nonce_proof(const TopicProofCache& cache, const NonceProof& nonce_proof,
736 purify_secp_context* secp_context,
737 ExperimentalBulletproofBackendCache* circuit_cache) const {
738 return api_impl::verify_topic_nonce_proof(cache, *this, nonce_proof, secp_context, circuit_cache);
739}
740
741Result<bool> PublicKey::verify_message_signature_with_proof(std::span<const unsigned char> message,
742 const ProvenSignature& signature,
743 purify_secp_context* secp_context,
744 ExperimentalBulletproofBackendCache* circuit_cache) const {
745 return api_impl::verify_message_signature_with_proof(*this, message, signature, secp_context, circuit_cache);
746}
747
748Result<bool> PublicKey::verify_message_signature_with_proof(const MessageProofCache& cache,
749 const ProvenSignature& signature,
750 purify_secp_context* secp_context,
751 ExperimentalBulletproofBackendCache* circuit_cache) const {
752 return api_impl::verify_message_signature_with_proof(cache, *this, signature, secp_context, circuit_cache);
753}
754
755Result<bool> PublicKey::verify_topic_signature_with_proof(std::span<const unsigned char> message,
756 std::span<const unsigned char> topic,
757 const ProvenSignature& signature,
758 purify_secp_context* secp_context,
759 ExperimentalBulletproofBackendCache* circuit_cache) const {
760 return api_impl::verify_topic_signature_with_proof(*this, message, topic, signature, secp_context, circuit_cache);
761}
762
763Result<bool> PublicKey::verify_topic_signature_with_proof(const TopicProofCache& cache,
764 std::span<const unsigned char> message,
765 const ProvenSignature& signature,
766 purify_secp_context* secp_context,
767 ExperimentalBulletproofBackendCache* circuit_cache) const {
768 return api_impl::verify_topic_signature_with_proof(cache, *this, message, signature, secp_context, circuit_cache);
769}
770
771Result<MessageProofCache> MessageProofCache::build(std::span<const unsigned char> message) {
772 return api_impl::build_message_proof_cache(message);
773}
774
775Result<TopicProofCache> TopicProofCache::build(std::span<const unsigned char> topic) {
776 return api_impl::build_topic_proof_cache(topic);
777}
778
779Result<KeyPair> KeyPair::from_secret(const SecretKey& secret, purify_secp_context* secp_context) {
780 PURIFY_ASSIGN_OR_RETURN(auto owned_secret, secret.clone(), "KeyPair::from_secret:clone");
781 return KeyPair::from_secret(std::move(owned_secret), secp_context);
782}
783
784Result<KeyPair> KeyPair::from_secret(SecretKey&& secret, purify_secp_context* secp_context) {
785 PURIFY_ASSIGN_OR_RETURN(auto public_key, PublicKey::from_secret(secret, secp_context), "KeyPair::from_secret:from_secret");
786 return KeyPair(std::move(secret), std::move(public_key));
787}
788
789Result<PreparedNonce> KeyPair::prepare_message_nonce(std::span<const unsigned char> message,
790 purify_secp_context* secp_context) const {
791 return api_impl::prepare_message_nonce(secret_, message, secp_context);
792}
793
794Result<PreparedNonceWithProof> KeyPair::prepare_message_nonce_with_proof(std::span<const unsigned char> message,
795 purify_secp_context* secp_context) const {
796 return api_impl::prepare_message_nonce_with_proof(secret_, message, secp_context);
797}
798
799Result<PreparedNonceWithProof> KeyPair::prepare_message_nonce_with_proof(const MessageProofCache& cache,
800 purify_secp_context* secp_context) const {
801 return api_impl::prepare_message_nonce_with_proof(secret_, cache, secp_context);
802}
803
804Result<PreparedNonce> KeyPair::prepare_topic_nonce(std::span<const unsigned char> topic,
805 purify_secp_context* secp_context) const {
806 return api_impl::prepare_topic_nonce(secret_, topic, secp_context);
807}
808
809Result<PreparedNonceWithProof> KeyPair::prepare_topic_nonce_with_proof(std::span<const unsigned char> topic,
810 purify_secp_context* secp_context) const {
811 return api_impl::prepare_topic_nonce_with_proof(secret_, topic, secp_context);
812}
813
814Result<PreparedNonceWithProof> KeyPair::prepare_topic_nonce_with_proof(const TopicProofCache& cache,
815 purify_secp_context* secp_context) const {
816 return api_impl::prepare_topic_nonce_with_proof(secret_, cache, secp_context);
817}
818
819Result<Signature> KeyPair::sign_message(std::span<const unsigned char> message,
820 purify_secp_context* secp_context) const {
821 PURIFY_ASSIGN_OR_RETURN(auto prepared, prepare_message_nonce(message, secp_context), "KeyPair::sign_message:prepare_message_nonce");
822 return sign_message_with_prepared(message, std::move(prepared), secp_context);
823}
824
825Result<Signature> KeyPair::sign_message_with_prepared(std::span<const unsigned char> message,
826 PreparedNonce&& prepared,
827 purify_secp_context* secp_context) const {
828 PURIFY_ASSIGN_OR_RETURN(const auto& signer, derive_bip340_key(secret_, secp_context), "KeyPair::sign_message_with_prepared:derive_bip340_key");
829 return std::move(prepared).sign_message(signer, message, secp_context);
830}
831
832Result<ProvenSignature> KeyPair::sign_message_with_prepared_proof(std::span<const unsigned char> message,
833 PreparedNonceWithProof&& prepared,
834 purify_secp_context* secp_context) const {
835 return std::move(prepared).sign_message(secret_, message, secp_context);
836}
837
838Result<Signature> KeyPair::sign_with_topic(std::span<const unsigned char> message,
839 std::span<const unsigned char> topic,
840 purify_secp_context* secp_context) const {
841 PURIFY_ASSIGN_OR_RETURN(auto prepared, prepare_topic_nonce(topic, secp_context), "KeyPair::sign_with_topic:prepare_topic_nonce");
842 return sign_with_prepared_topic(message, std::move(prepared), secp_context);
843}
844
845Result<Signature> KeyPair::sign_with_prepared_topic(std::span<const unsigned char> message,
846 PreparedNonce&& prepared,
847 purify_secp_context* secp_context) const {
848 PURIFY_ASSIGN_OR_RETURN(const auto& signer, derive_bip340_key(secret_, secp_context), "KeyPair::sign_with_prepared_topic:derive_bip340_key");
849 return std::move(prepared).sign_topic_message(signer, message, secp_context);
850}
851
852Result<ProvenSignature> KeyPair::sign_with_prepared_topic_proof(std::span<const unsigned char> message,
853 PreparedNonceWithProof&& prepared,
854 purify_secp_context* secp_context) const {
855 return std::move(prepared).sign_topic_message(secret_, message, secp_context);
856}
857
858Result<ProvenSignature> KeyPair::sign_message_with_proof(std::span<const unsigned char> message,
859 purify_secp_context* secp_context) const {
860 PURIFY_ASSIGN_OR_RETURN(auto prepared, prepare_message_nonce_with_proof(message, secp_context),
861 "KeyPair::sign_message_with_proof:prepare_message_nonce_with_proof");
862 return sign_message_with_prepared_proof(message, std::move(prepared), secp_context);
863}
864
865Result<ProvenSignature> KeyPair::sign_message_with_proof(const MessageProofCache& cache,
866 purify_secp_context* secp_context) const {
867 PURIFY_ASSIGN_OR_RETURN(auto prepared, prepare_message_nonce_with_proof(cache, secp_context),
868 "KeyPair::sign_message_with_proof:prepare_message_nonce_with_proof");
869 return sign_message_with_prepared_proof(cache.message, std::move(prepared), secp_context);
870}
871
872Result<ProvenSignature> KeyPair::sign_with_topic_proof(std::span<const unsigned char> message,
873 std::span<const unsigned char> topic,
874 purify_secp_context* secp_context) const {
875 PURIFY_ASSIGN_OR_RETURN(auto prepared, prepare_topic_nonce_with_proof(topic, secp_context),
876 "KeyPair::sign_with_topic_proof:prepare_topic_nonce_with_proof");
877 return sign_with_prepared_topic_proof(message, std::move(prepared), secp_context);
878}
879
880Result<ProvenSignature> KeyPair::sign_with_topic_proof(std::span<const unsigned char> message,
881 const TopicProofCache& cache,
882 purify_secp_context* secp_context) const {
883 PURIFY_ASSIGN_OR_RETURN(auto prepared, prepare_topic_nonce_with_proof(cache, secp_context),
884 "KeyPair::sign_with_topic_proof:prepare_topic_nonce_with_proof");
885 return sign_with_prepared_topic_proof(message, std::move(prepared), secp_context);
886}
887
888} // namespace purify::puresign
C++ wrappers for the BPPP functionality used by Purify.
Purify result carrier that either holds a value or an error.
Definition expected.hpp:64
Caller-owned cache for reusable legacy Bulletproof backend resources keyed by gate count.
Result< NativeBulletproofCircuit::PackedWithSlack > instantiate_packed(const UInt512 &pubkey) const
Move-only packed Purify secret stored in dedicated heap memory.
Definition secret.hpp:52
Result< SecretKey > clone() const
Creates a second owned copy of this secret key.
Definition secret.hpp:85
Reusable BIP340-style tagged SHA-256 helper.
Definition curve.hpp:111
Move-only prepared nonce bundled with its public statement proof.
Definition legacy.hpp:376
Move-only prepared nonce bound to either a message or a topic.
Definition legacy.hpp:292
PreparedNonce(const PreparedNonce &)=delete
Elliptic-curve helpers, fixed parameters, and hash-to-curve utilities for Purify.
#define PURIFY_RETURN_IF_ERROR(expr, context)
Evaluates an expected-like expression and returns the wrapped error on failure.
Definition error.hpp:329
#define PURIFY_ASSIGN_OR_RETURN(lhs, expr, context)
Evaluates an expected-like expression, binds the value to lhs, and propagates errors.
Definition error.hpp:338
Scalar32 scalar
Definition legacy.cpp:128
Nonce nonce
Definition legacy.cpp:129
XOnly32 signer_pubkey
Definition legacy.cpp:130
XOnly32 binding_digest
Definition legacy.cpp:131
PreparedNonce::Scope scope
Definition legacy.cpp:127
Legacy Bulletproof-backed Purify-derived BIP340 signing helpers with prepared nonces.
GeneratorBytes base_generator(purify_secp_context *secp_context)
Returns the serialized secp256k1 base generator used as the blind generator.
Definition bppp.cpp:1064
const UInt256 & secp256k1_order()
Definition c_api.cpp:62
Bytes tagged_eval_input(std::string_view tag, std::span< const unsigned char > input)
Definition common.hpp:21
Status validate_topic_proof_cache(const CacheLike &cache, std::string_view nonce_tag)
Definition common.hpp:92
void secure_clear_bytes(void *data, std::size_t size) noexcept
Definition secret.hpp:22
Status validate_message_proof_cache(const CacheLike &cache, std::string_view nonce_tag)
Definition common.hpp:78
Status validate_proof_cache_circuit(const CircuitLike &circuit, const char *context)
Definition common.hpp:64
Result< Signature > sign_message_with_prepared(const SecretKey &secret, std::span< const unsigned char > message, PreparedNonce &&prepared, purify_secp_context *secp_context)
Definition legacy.cpp:515
Result< Signature > sign_with_prepared_topic(const SecretKey &secret, std::span< const unsigned char > message, PreparedNonce &&prepared, purify_secp_context *secp_context)
Definition legacy.cpp:534
std::array< unsigned char, 32 > Scalar32
Definition legacy.hpp:25
std::array< unsigned char, 32 > XOnly32
Definition legacy.hpp:26
Status require_secp_context(const purify_secp_context *context, const char *error_context)
Definition common.hpp:56
constexpr Unexpected< Error > unexpected_error(ErrorCode code, const char *context=nullptr)
Constructs an unexpected Error value from a machine-readable code.
Definition error.hpp:293
const UInt256 & prime_p()
Returns the Purify base-field modulus.
Definition curve.cpp:199
Result< NativeBulletproofCircuitTemplate > verifier_circuit_template(const Bytes &message)
Builds a reusable public-key-agnostic verifier-circuit template for a message.
Definition api.cpp:210
Result< BulletproofWitnessData > prove_assignment_data(const Bytes &message, const SecretKey &secret)
Computes the native Purify witness for a message and secret.
Definition api.cpp:236
Result< bool > verify_experimental_circuit(const NativeBulletproofCircuit &circuit, const ExperimentalBulletproofProof &proof, const BulletproofGeneratorBytes &value_generator, purify_secp_context *secp_context, std::span< const unsigned char > statement_binding={}, ExperimentalBulletproofBackendCache *backend_cache=nullptr)
Verifies a proof produced by prove_experimental_circuit against the same one-commitment native circui...
Result< NativeBulletproofCircuit > verifier_circuit(const Bytes &message, const UInt512 &pubkey)
Builds the native verifier circuit for a message and public key.
Definition api.cpp:204
Bytes bytes_from_ascii(std::string_view input)
Encodes an ASCII string as a byte vector.
Definition curve.cpp:163
Status validate_public_key(const UInt512 &packed)
Validates the packed public-key encoding range.
Definition curve.cpp:314
Result< Bip340Key > derive_bip340_key(const SecretKey &secret, purify_secp_context *secp_context)
Derives a canonical BIP340 signing keypair from an owned Purify secret.
Definition api.cpp:161
Result< GeneratedKey > derive_key(const SecretKey &secret)
Derives the packed public key corresponding to a packed secret.
Definition api.cpp:142
std::vector< unsigned char > Bytes
Dynamically sized byte string used for messages, serialized witnesses, and proofs.
Definition common.hpp:99
Result< FieldElement > eval(const SecretKey &secret, const Bytes &message)
Evaluates the Purify PRF for an owned secret key and message.
Definition api.cpp:177
Result< ExperimentalBulletproofProof > prove_experimental_circuit_assume_valid(const NativeBulletproofCircuit::PackedWithSlack &circuit, const BulletproofAssignmentData &assignment, const BulletproofScalarBytes &nonce, const BulletproofGeneratorBytes &value_generator, purify_secp_context *secp_context, std::span< const unsigned char > statement_binding, std::optional< BulletproofScalarBytes > blind, ExperimentalBulletproofBackendCache *backend_cache)
BigUInt< 4 > UInt256
256-bit unsigned integer used for field elements and curve orders.
Definition numeric.hpp:798
Expected< void, Error > Status
Expected-returning convenience alias for Purify status-only APIs.
Definition error.hpp:102
Scalar32 scalar
Definition bppp.cpp:119
Scope scope
Definition bppp.cpp:118
Nonce nonce
Definition bppp.cpp:120
XOnly32 signer_pubkey
Definition bppp.cpp:121
Bytes eval_input
Definition bppp.cpp:123
XOnly32 binding_digest
Definition bppp.cpp:122
int purify_bip340_xonly_from_point(purify_secp_context *context, const unsigned char point33[33], unsigned char xonly32[32], int *parity_out)
Converts a compressed secp256k1 point into its x-only public key encoding.
int purify_bip340_nonce_from_scalar(purify_secp_context *context, unsigned char scalar32[32], unsigned char xonly_nonce32[32])
Canonicalizes a valid secp256k1 nonce scalar for BIP340 and derives its x-only public nonce.
int purify_bip340_sign_with_fixed_nonce(purify_secp_context *context, unsigned char sig64[64], const unsigned char *msg, size_t msglen, const unsigned char seckey32[32], const unsigned char nonce32[32])
Signs a message with a caller-supplied BIP340 nonce scalar.
int purify_bip340_validate_signature(purify_secp_context *context, const unsigned char sig64[64])
Returns nonzero when the 64-byte BIP340 signature has a syntactically valid encoding.
int purify_bip340_validate_xonly_pubkey(purify_secp_context *context, const unsigned char xonly_pubkey32[32])
Returns nonzero when the x-only public key encoding parses successfully.
int purify_bip340_verify(purify_secp_context *context, const unsigned char sig64[64], const unsigned char *msg, size_t msglen, const unsigned char xonly_pubkey32[32])
Verifies a BIP340 signature against a serialized x-only public key.
static BigUInt from_hex(std::string_view hex)
Parses a hexadecimal string with the precondition that the value fits exactly.
Definition numeric.hpp:280
Canonical BIP340 keypair derived deterministically from a packed Purify secret.
Definition api.hpp:38
Cacheable message-bound nonce-proof template.
Definition legacy.hpp:247
NativeBulletproofCircuitTemplate circuit_template
Definition legacy.hpp:250
ExperimentalBulletproofBackendCache backend_cache
Definition legacy.hpp:252
Public nonce together with its experimental Purify statement proof.
Definition legacy.hpp:221
Public BIP340 nonce point in x-only form.
Definition legacy.hpp:190
Standard signature bundled with the public nonce proof it relied on.
Definition legacy.hpp:231
Public key bundle pairing a Purify packed public key with its derived BIP340 x-only key.
Definition legacy.hpp:42
Standard 64-byte BIP340 signature.
Definition legacy.hpp:201
Cacheable topic-bound nonce-proof template.
Definition legacy.hpp:269
NativeBulletproofCircuitTemplate circuit_template
Definition legacy.hpp:272
ExperimentalBulletproofBackendCache backend_cache
Definition legacy.hpp:274