purify
C++ Purify implementation with native circuit and BPP support
Loading...
Searching...
No Matches
bppp.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 <array>
14#include <span>
15#include <string_view>
16
17#include "detail/common.hpp"
18#include "purify/secp_bridge.h"
19
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/BPPP/Proof/Message/V1";
38constexpr std::string_view kTopicProofTag = "PureSign/BPPP/Proof/Topic/V1";
39
40using Scope = PreparedNonce::Scope;
41
42const TaggedHash& binding_tagged_hash(Scope scope) {
43 static const TaggedHash kMessageBindingTaggedHash(kMessageBindingTag);
44 static const TaggedHash kTopicBindingTaggedHash(kTopicBindingTag);
45 return scope == Scope::Message ? kMessageBindingTaggedHash : kTopicBindingTaggedHash;
46}
47
48const TaggedHash& proof_tagged_hash(Scope scope) {
49 static const TaggedHash kMessageProofTaggedHash(kMessageProofTag);
50 static const TaggedHash kTopicProofTaggedHash(kTopicProofTag);
51 return scope == Scope::Message ? kMessageProofTaggedHash : kTopicProofTaggedHash;
52}
53
54const UInt256& secp256k1_order() {
55 static const UInt256 value =
56 UInt256::from_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141");
57 return value;
58}
59
60Status validate_puresign_field_alignment() {
61 if (prime_p() != secp256k1_order()) {
62 return unexpected_error(ErrorCode::InternalMismatch, "puresign_plusplus:field_order_mismatch");
63 }
64 return {};
65}
66
67const unsigned char* nullable_data(std::span<const unsigned char> input) {
68 return input.empty() ? nullptr : input.data();
69}
70
71XOnly32 binding_digest(const TaggedHash& tag, std::span<const unsigned char> input) {
72 XOnly32 out{};
73 out = tag.digest(input);
74 return out;
75}
76
77std::string_view proof_tag_for_scope(Scope scope) {
78 return scope == Scope::Message ? kMessageProofTag : kTopicProofTag;
79}
80
81Bytes proof_statement_binding(Scope scope) {
82 return bytes_from_ascii(proof_tag_for_scope(scope));
83}
84
85Scalar32 derive_proof_nonce_seed(const SecretKey& secret, Scope scope, std::span<const unsigned char> eval_input) {
86 std::array<unsigned char, 64> secret_bytes = secret.packed().to_bytes_be();
87 Scalar32 out{};
88 std::array digest_segments{
89 std::span<const unsigned char>(secret_bytes.data(), secret_bytes.size()),
91 };
92 out = proof_tagged_hash(scope).digest_many(digest_segments);
93 detail::secure_clear_bytes(secret_bytes.data(), secret_bytes.size());
94 return out;
95}
96
97Status validate_public_key_bundle(const PublicKey& public_key, purify_secp_context* secp_context) {
98 PURIFY_RETURN_IF_ERROR(validate_public_key(public_key.purify_pubkey),
99 "puresign_plusplus:validate_public_key_bundle:purify_pubkey");
100 PURIFY_RETURN_IF_ERROR(require_secp_context(secp_context, "puresign_plusplus:validate_public_key_bundle:secp_context"),
101 "puresign_plusplus:validate_public_key_bundle:secp_context");
102 if (purify_bip340_validate_xonly_pubkey(secp_context, public_key.bip340_pubkey.data()) == 0) {
104 "puresign_plusplus:validate_public_key_bundle:bip340_pubkey");
105 }
106 return {};
107}
108
109Status validate_message_proof_cache(const MessageProofCache& cache) {
110 return detail::validate_message_proof_cache(cache, kMessageNonceTag);
111}
112
113Status validate_topic_proof_cache(const TopicProofCache& cache) {
114 return detail::validate_topic_proof_cache(cache, kTopicNonceTag);
115}
116
117struct DerivedNonceData {
118 Scope scope = Scope::Message;
120 Nonce nonce{};
124};
125
126Result<DerivedNonceData> derive_nonce_data(const SecretKey& secret,
127 Scope scope,
128 std::span<const unsigned char> input,
129 purify_secp_context* secp_context) {
130 PURIFY_RETURN_IF_ERROR(validate_puresign_field_alignment(), "derive_nonce_data:validate_puresign_field_alignment");
131 PURIFY_ASSIGN_OR_RETURN(const auto& signer, derive_bip340_key(secret, secp_context), "derive_nonce_data:derive_bip340_key");
132
133 const std::string_view nonce_tag = scope == Scope::Message ? kMessageNonceTag : kTopicNonceTag;
134 const TaggedHash& binding_hash = binding_tagged_hash(scope);
135
136 DerivedNonceData out{};
137 out.scope = scope;
138 out.signer_pubkey = signer.xonly_pubkey;
139 out.binding_digest = binding_digest(binding_hash, input);
140 out.eval_input = detail::tagged_eval_input(nonce_tag, input);
141
142 PURIFY_ASSIGN_OR_RETURN(const auto& nonce_value, eval(secret, out.eval_input), "derive_nonce_data:eval");
143 out.scalar = nonce_value.to_bytes_be();
144 if (std::all_of(out.scalar.begin(), out.scalar.end(), [](unsigned char byte) { return byte == 0; })) {
145 return unexpected_error(ErrorCode::BackendRejectedInput, "derive_nonce_data:zero_nonce");
146 }
147
148 PURIFY_RETURN_IF_ERROR(require_secp_context(secp_context, "derive_nonce_data:secp_context"),
149 "derive_nonce_data:secp_context");
150 if (purify_bip340_nonce_from_scalar(secp_context, out.scalar.data(), out.nonce.xonly.data()) == 0) {
151 return unexpected_error(ErrorCode::BackendRejectedInput, "derive_nonce_data:bip340_nonce_from_scalar");
152 }
153 return out;
154}
155
156Result<DerivedNonceData> prepare_nonce_data_impl(const SecretKey& secret,
157 Scope scope,
158 std::span<const unsigned char> input,
159 purify_secp_context* secp_context) {
160 if (scope == Scope::Topic && input.empty()) {
161 return unexpected_error(ErrorCode::EmptyInput, "prepare_nonce_data_impl:empty_topic");
162 }
163 PURIFY_ASSIGN_OR_RETURN(auto nonce_data, derive_nonce_data(secret, scope, input, secp_context),
164 "prepare_nonce_data_impl:derive_nonce_data");
165 return nonce_data;
166}
167
168Result<bppp::PointBytes> commitment_point_from_scalar(const FieldElement& scalar,
169 purify_secp_context* secp_context) {
170 Scalar32 blind{};
171 return bppp::pedersen_commit_char(blind, bppp::scalar_bytes(scalar), secp_context,
172 bppp::base_generator(secp_context), bppp::base_generator(secp_context));
173}
174
175Result<bool> nonce_proof_matches_nonce(const NonceProof& nonce_proof,
176 purify_secp_context* secp_context) {
177 XOnly32 xonly{};
178 int parity = 0;
179 PURIFY_RETURN_IF_ERROR(require_secp_context(secp_context, "nonce_proof_matches_nonce:secp_context"),
180 "nonce_proof_matches_nonce:secp_context");
181 if (purify_bip340_xonly_from_point(secp_context, nonce_proof.commitment_point.data(), xonly.data(), &parity) == 0) {
183 "nonce_proof_matches_nonce:invalid_commitment_point");
184 }
185 (void)parity;
186 return xonly == nonce_proof.nonce.xonly;
187}
188
189Result<NonceProof> build_nonce_proof_from_template(const SecretKey& secret,
190 Scope scope,
191 const Nonce& expected_nonce,
192 std::span<const unsigned char> eval_input,
193 const NativeBulletproofCircuitTemplate& circuit_template,
194 purify_secp_context* secp_context,
195 bppp::ExperimentalCircuitBackend* circuit_cache) {
197 "build_nonce_proof_from_template:prove_assignment_data");
198 PURIFY_ASSIGN_OR_RETURN(auto partial_ok, circuit_template.partial_evaluate(witness.assignment),
199 "build_nonce_proof_from_template:partial_evaluate");
200 if (!partial_ok) {
201 return unexpected_error(ErrorCode::BindingMismatch, "build_nonce_proof_from_template:partial_evaluate_false");
202 }
203 PURIFY_ASSIGN_OR_RETURN(auto final_ok, circuit_template.final_evaluate(witness.assignment, witness.public_key),
204 "build_nonce_proof_from_template:final_evaluate");
205 if (!final_ok) {
206 return unexpected_error(ErrorCode::BindingMismatch, "build_nonce_proof_from_template:final_evaluate_false");
207 }
208 PURIFY_ASSIGN_OR_RETURN(const auto& circuit, circuit_template.instantiate(witness.public_key),
209 "build_nonce_proof_from_template:instantiate");
210 PURIFY_RETURN_IF_ERROR(detail::validate_proof_cache_circuit(circuit, "build_nonce_proof_from_template:circuit_shape"),
211 "build_nonce_proof_from_template:validate_proof_cache_circuit");
212
213 PURIFY_ASSIGN_OR_RETURN(auto commitment_point, commitment_point_from_scalar(witness.assignment.commitments.front(), secp_context),
214 "build_nonce_proof_from_template:commitment_point_from_scalar");
215 Scalar32 proof_nonce = derive_proof_nonce_seed(secret, scope, eval_input);
216 Bytes statement_binding = proof_statement_binding(scope);
217 std::array<bppp::PointBytes, 1> public_commitments{commitment_point};
219 auto proof,
221 circuit, witness.assignment, proof_nonce, public_commitments, secp_context, statement_binding, circuit_cache),
222 "build_nonce_proof_from_template:prove_experimental_circuit_zk_norm_arg_with_public_commitments");
223
224 NonceProof out{};
225 out.nonce = expected_nonce;
226 out.commitment_point = commitment_point;
227 out.proof = std::move(proof);
228 PURIFY_ASSIGN_OR_RETURN(auto match, nonce_proof_matches_nonce(out, secp_context),
229 "build_nonce_proof_from_template:nonce_proof_matches_nonce");
230 if (!match) {
231 return unexpected_error(ErrorCode::InternalMismatch, "build_nonce_proof_from_template:nonce_mismatch");
232 }
233 return out;
234}
235
236Result<NonceProof> build_nonce_proof(const SecretKey& secret,
237 Scope scope,
238 std::span<const unsigned char> input,
239 const Nonce& expected_nonce,
240 purify_secp_context* secp_context,
241 bppp::ExperimentalCircuitBackend* circuit_cache) {
242 const std::string_view nonce_tag = scope == Scope::Message ? kMessageNonceTag : kTopicNonceTag;
243 PURIFY_ASSIGN_OR_RETURN(const auto& circuit_template, verifier_circuit_template(detail::tagged_eval_input(nonce_tag, input)),
244 "build_nonce_proof:verifier_circuit_template");
245 Bytes eval_input = detail::tagged_eval_input(nonce_tag, input);
246 return build_nonce_proof_from_template(secret, scope, expected_nonce, eval_input, circuit_template, secp_context,
247 circuit_cache);
248}
249
250Result<bool> verify_nonce_proof_with_circuit(const PublicKey& public_key,
251 const NativeBulletproofCircuit& circuit,
252 const NonceProof& nonce_proof,
253 Scope scope,
254 purify_secp_context* secp_context,
255 const char* context,
256 bppp::ExperimentalCircuitBackend* circuit_cache) {
257 PURIFY_RETURN_IF_ERROR(validate_public_key_bundle(public_key, secp_context), context);
259 PURIFY_ASSIGN_OR_RETURN(auto match, nonce_proof_matches_nonce(nonce_proof, secp_context), context);
260 if (!match) {
261 return false;
262 }
263 Bytes statement_binding = proof_statement_binding(scope);
264 std::array<bppp::PointBytes, 1> public_commitments{nonce_proof.commitment_point};
266 circuit, nonce_proof.proof, public_commitments, secp_context, statement_binding, circuit_cache);
267}
268
269} // namespace
270
271Result<MessageProofCache> MessageProofCache::build(std::span<const unsigned char> message) {
274 "MessageProofCache::build:verifier_circuit_template");
276 "MessageProofCache::build:integrity_digest");
277 MessageProofCache cache{};
279 cache.eval_input = std::move(eval_input);
280 cache.circuit_template = std::move(circuit_template);
281 cache.template_digest = std::move(template_digest);
282 return cache;
283}
284
285Result<TopicProofCache> TopicProofCache::build(std::span<const unsigned char> topic) {
286 if (topic.empty()) {
287 return unexpected_error(ErrorCode::EmptyInput, "TopicProofCache::build:empty_topic");
288 }
291 "TopicProofCache::build:verifier_circuit_template");
293 "TopicProofCache::build:integrity_digest");
294 TopicProofCache cache{};
296 cache.eval_input = std::move(eval_input);
297 cache.circuit_template = std::move(circuit_template);
298 cache.template_digest = std::move(template_digest);
299 return cache;
300}
301
304 : scope_(scope), scalar_(scalar), nonce_(nonce), signer_pubkey_(signer_pubkey), binding_digest_(binding_digest) {}
305
310
312 : scope_(other.scope_), scalar_(other.scalar_), nonce_(other.nonce_),
313 signer_pubkey_(other.signer_pubkey_), binding_digest_(other.binding_digest_) {
314 other.clear();
315}
316
318 if (this != &other) {
319 clear();
320 scope_ = other.scope_;
321 scalar_ = other.scalar_;
322 nonce_ = other.nonce_;
323 signer_pubkey_ = other.signer_pubkey_;
324 binding_digest_ = other.binding_digest_;
325 other.clear();
326 }
327 return *this;
328}
329
331 clear();
332}
333
334void PreparedNonce::clear() noexcept {
335 std::fill(scalar_.begin(), scalar_.end(), static_cast<unsigned char>(0));
336}
337
339 std::span<const unsigned char> message,
340 purify_secp_context* secp_context) && {
341 if (scope_ != Scope::Message) {
342 return unexpected_error(ErrorCode::BindingMismatch, "PreparedNonce::sign_message:scope");
343 }
344 if (signer_pubkey_ != signer.xonly_pubkey) {
345 return unexpected_error(ErrorCode::BindingMismatch, "PreparedNonce::sign_message:signer_pubkey");
346 }
347 if (binding_digest_ != binding_digest(binding_tagged_hash(Scope::Message), message)) {
348 return unexpected_error(ErrorCode::BindingMismatch, "PreparedNonce::sign_message:message_binding");
349 }
350
351 PURIFY_RETURN_IF_ERROR(require_secp_context(secp_context, "PreparedNonce::sign_message:secp_context"),
352 "PreparedNonce::sign_message:secp_context");
353 Signature out{};
354 if (purify_bip340_sign_with_fixed_nonce(secp_context, out.bytes.data(), nullable_data(message), message.size(),
355 signer.seckey.data(), scalar_.data()) == 0) {
356 return unexpected_error(ErrorCode::BackendRejectedInput, "PreparedNonce::sign_message:sign_with_fixed_nonce");
357 }
358 if (out.nonce().xonly != nonce_.xonly) {
359 return unexpected_error(ErrorCode::InternalMismatch, "PreparedNonce::sign_message:nonce_mismatch");
360 }
361 if (purify_bip340_verify(secp_context, out.bytes.data(), nullable_data(message), message.size(),
362 signer.xonly_pubkey.data()) == 0) {
363 return unexpected_error(ErrorCode::InternalMismatch, "PreparedNonce::sign_message:self_verify");
364 }
365 return out;
366}
367
369 std::span<const unsigned char> message,
370 purify_secp_context* secp_context) && {
371 if (scope_ != Scope::Topic) {
372 return unexpected_error(ErrorCode::BindingMismatch, "PreparedNonce::sign_topic_message:scope");
373 }
374 if (signer_pubkey_ != signer.xonly_pubkey) {
375 return unexpected_error(ErrorCode::BindingMismatch, "PreparedNonce::sign_topic_message:signer_pubkey");
376 }
377
378 PURIFY_RETURN_IF_ERROR(require_secp_context(secp_context, "PreparedNonce::sign_topic_message:secp_context"),
379 "PreparedNonce::sign_topic_message:secp_context");
380 Signature out{};
381 if (purify_bip340_sign_with_fixed_nonce(secp_context, out.bytes.data(), nullable_data(message), message.size(),
382 signer.seckey.data(), scalar_.data()) == 0) {
384 "PreparedNonce::sign_topic_message:sign_with_fixed_nonce");
385 }
386 if (out.nonce().xonly != nonce_.xonly) {
387 return unexpected_error(ErrorCode::InternalMismatch, "PreparedNonce::sign_topic_message:nonce_mismatch");
388 }
389 if (purify_bip340_verify(secp_context, out.bytes.data(), nullable_data(message), message.size(),
390 signer.xonly_pubkey.data()) == 0) {
391 return unexpected_error(ErrorCode::InternalMismatch, "PreparedNonce::sign_topic_message:self_verify");
392 }
393 return out;
394}
395
397 return PreparedNonceWithProof(std::move(prepared), std::move(proof));
398}
399
401 std::span<const unsigned char> message,
402 purify_secp_context* secp_context) && {
403 NonceProof nonce_proof = std::move(proof_);
404 PURIFY_ASSIGN_OR_RETURN(auto signature,
405 api_impl::sign_message_with_prepared(secret, message, std::move(prepared_), secp_context),
406 "PreparedNonceWithProof::sign_message:sign_message_with_prepared");
407 if (signature.nonce().xonly != nonce_proof.nonce.xonly) {
408 return unexpected_error(ErrorCode::InternalMismatch, "PreparedNonceWithProof::sign_message:nonce_mismatch");
409 }
410 return ProvenSignature{signature, std::move(nonce_proof)};
411}
412
414 std::span<const unsigned char> message,
415 purify_secp_context* secp_context) && {
416 NonceProof nonce_proof = std::move(proof_);
417 PURIFY_ASSIGN_OR_RETURN(auto signature,
418 api_impl::sign_with_prepared_topic(secret, message, std::move(prepared_), secp_context),
419 "PreparedNonceWithProof::sign_topic_message:sign_with_prepared_topic");
420 if (signature.nonce().xonly != nonce_proof.nonce.xonly) {
421 return unexpected_error(ErrorCode::InternalMismatch, "PreparedNonceWithProof::sign_topic_message:nonce_mismatch");
422 }
423 return ProvenSignature{signature, std::move(nonce_proof)};
424}
425
426namespace api_impl {
427
429 PURIFY_ASSIGN_OR_RETURN(const auto& purify_key, derive_key(secret), "derive_public_key:derive_key");
430 PURIFY_ASSIGN_OR_RETURN(const auto& bip340_key, derive_bip340_key(secret, secp_context), "derive_public_key:derive_bip340_key");
431 return PublicKey{purify_key.public_key, bip340_key.xonly_pubkey};
432}
433
434Result<MessageProofCache> build_message_proof_cache(std::span<const unsigned char> message) {
435 return MessageProofCache::build(message);
436}
437
438Result<TopicProofCache> build_topic_proof_cache(std::span<const unsigned char> topic) {
439 return TopicProofCache::build(topic);
440}
441
442Result<PreparedNonce> prepare_message_nonce(const SecretKey& secret, std::span<const unsigned char> message,
443 purify_secp_context* secp_context) {
444 PURIFY_ASSIGN_OR_RETURN(const auto& nonce_data, prepare_nonce_data_impl(secret, Scope::Message, message, secp_context),
445 "prepare_message_nonce:prepare_nonce_data_impl");
446 return PreparedNonce::from_parts(Scope::Message, nonce_data.scalar, nonce_data.nonce,
447 nonce_data.signer_pubkey, nonce_data.binding_digest);
448}
449
451 std::span<const unsigned char> message,
452 purify_secp_context* secp_context,
453 bppp::ExperimentalCircuitBackend* circuit_cache) {
454 PURIFY_ASSIGN_OR_RETURN(const auto& nonce_data, prepare_nonce_data_impl(secret, Scope::Message, message, secp_context),
455 "prepare_message_nonce_with_proof:prepare_nonce_data_impl");
456 PreparedNonce prepared = PreparedNonce::from_parts(Scope::Message, nonce_data.scalar,
457 nonce_data.nonce, nonce_data.signer_pubkey,
458 nonce_data.binding_digest);
459 PURIFY_ASSIGN_OR_RETURN(auto proof, build_nonce_proof(secret, Scope::Message, message, prepared.public_nonce(), secp_context,
460 circuit_cache),
461 "prepare_message_nonce_with_proof:build_nonce_proof");
462 return PreparedNonceWithProof::from_parts(std::move(prepared), std::move(proof));
463}
464
466 const MessageProofCache& cache,
467 purify_secp_context* secp_context,
468 bppp::ExperimentalCircuitBackend* circuit_cache) {
469 PURIFY_RETURN_IF_ERROR(validate_message_proof_cache(cache),
470 "prepare_message_nonce_with_proof:validate_message_proof_cache");
471 PURIFY_ASSIGN_OR_RETURN(const auto& nonce_data, prepare_nonce_data_impl(secret, Scope::Message, cache.message, secp_context),
472 "prepare_message_nonce_with_proof:prepare_nonce_data_impl");
473 PreparedNonce prepared = PreparedNonce::from_parts(Scope::Message, nonce_data.scalar,
474 nonce_data.nonce, nonce_data.signer_pubkey,
475 nonce_data.binding_digest);
477 auto proof,
478 build_nonce_proof_from_template(secret, Scope::Message, prepared.public_nonce(),
479 cache.eval_input, cache.circuit_template, secp_context,
480 circuit_cache != nullptr ? circuit_cache : &cache.backend_cache),
481 "prepare_message_nonce_with_proof:build_nonce_proof_from_template");
482 return PreparedNonceWithProof::from_parts(std::move(prepared), std::move(proof));
483}
484
485Result<PreparedNonce> prepare_topic_nonce(const SecretKey& secret, std::span<const unsigned char> topic,
486 purify_secp_context* secp_context) {
487 PURIFY_ASSIGN_OR_RETURN(const auto& nonce_data, prepare_nonce_data_impl(secret, Scope::Topic, topic, secp_context),
488 "prepare_topic_nonce:prepare_nonce_data_impl");
489 return PreparedNonce::from_parts(Scope::Topic, nonce_data.scalar, nonce_data.nonce,
490 nonce_data.signer_pubkey, nonce_data.binding_digest);
491}
492
494 std::span<const unsigned char> topic,
495 purify_secp_context* secp_context,
496 bppp::ExperimentalCircuitBackend* circuit_cache) {
497 PURIFY_ASSIGN_OR_RETURN(const auto& nonce_data, prepare_nonce_data_impl(secret, Scope::Topic, topic, secp_context),
498 "prepare_topic_nonce_with_proof:prepare_nonce_data_impl");
499 PreparedNonce prepared = PreparedNonce::from_parts(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, build_nonce_proof(secret, Scope::Topic, topic, prepared.public_nonce(), secp_context,
503 circuit_cache),
504 "prepare_topic_nonce_with_proof:build_nonce_proof");
505 return PreparedNonceWithProof::from_parts(std::move(prepared), std::move(proof));
506}
507
509 const TopicProofCache& cache,
510 purify_secp_context* secp_context,
511 bppp::ExperimentalCircuitBackend* circuit_cache) {
512 PURIFY_RETURN_IF_ERROR(validate_topic_proof_cache(cache),
513 "prepare_topic_nonce_with_proof:validate_topic_proof_cache");
514 PURIFY_ASSIGN_OR_RETURN(const auto& nonce_data, prepare_nonce_data_impl(secret, Scope::Topic, cache.topic, secp_context),
515 "prepare_topic_nonce_with_proof:prepare_nonce_data_impl");
516 PreparedNonce prepared = PreparedNonce::from_parts(Scope::Topic, nonce_data.scalar,
517 nonce_data.nonce, nonce_data.signer_pubkey,
518 nonce_data.binding_digest);
520 auto proof,
521 build_nonce_proof_from_template(secret, Scope::Topic, prepared.public_nonce(),
522 cache.eval_input, cache.circuit_template, secp_context,
523 circuit_cache != nullptr ? circuit_cache : &cache.backend_cache),
524 "prepare_topic_nonce_with_proof:build_nonce_proof_from_template");
525 return PreparedNonceWithProof::from_parts(std::move(prepared), std::move(proof));
526}
527
528Result<Signature> sign_message(const SecretKey& secret, std::span<const unsigned char> message,
529 purify_secp_context* secp_context) {
530 PURIFY_ASSIGN_OR_RETURN(auto prepared, prepare_message_nonce(secret, message, secp_context), "sign_message:prepare_message_nonce");
531 return sign_message_with_prepared(secret, message, std::move(prepared), secp_context);
532}
533
534Result<Signature> sign_message_with_prepared(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_message_with_prepared:derive_bip340_key");
537 return std::move(prepared).sign_message(signer, message, secp_context);
538}
539
541 std::span<const unsigned char> message,
542 PreparedNonceWithProof&& prepared,
543 purify_secp_context* secp_context) {
544 return std::move(prepared).sign_message(secret, message, secp_context);
545}
546
547Result<Signature> sign_with_topic(const SecretKey& secret, std::span<const unsigned char> message,
548 std::span<const unsigned char> topic,
549 purify_secp_context* secp_context) {
550 PURIFY_ASSIGN_OR_RETURN(auto prepared, prepare_topic_nonce(secret, topic, secp_context), "sign_with_topic:prepare_topic_nonce");
551 return sign_with_prepared_topic(secret, message, std::move(prepared), secp_context);
552}
553
554Result<Signature> sign_with_prepared_topic(const SecretKey& secret, std::span<const unsigned char> message,
555 PreparedNonce&& prepared, purify_secp_context* secp_context) {
556 PURIFY_ASSIGN_OR_RETURN(const auto& signer, derive_bip340_key(secret, secp_context), "sign_with_prepared_topic:derive_bip340_key");
557 return std::move(prepared).sign_topic_message(signer, message, secp_context);
558}
559
561 std::span<const unsigned char> message,
562 PreparedNonceWithProof&& prepared,
563 purify_secp_context* secp_context) {
564 return std::move(prepared).sign_topic_message(secret, message, secp_context);
565}
566
567Result<ProvenSignature> sign_message_with_proof(const SecretKey& secret, std::span<const unsigned char> message,
568 purify_secp_context* secp_context,
569 bppp::ExperimentalCircuitBackend* circuit_cache) {
570 PURIFY_ASSIGN_OR_RETURN(auto prepared, prepare_message_nonce_with_proof(secret, message, secp_context, circuit_cache),
571 "sign_message_with_proof:prepare_message_nonce_with_proof");
572 return sign_message_with_prepared_proof(secret, message, std::move(prepared), secp_context);
573}
574
576 purify_secp_context* secp_context,
577 bppp::ExperimentalCircuitBackend* circuit_cache) {
578 PURIFY_ASSIGN_OR_RETURN(auto prepared, prepare_message_nonce_with_proof(secret, cache, secp_context, circuit_cache),
579 "sign_message_with_proof:prepare_message_nonce_with_proof");
580 return sign_message_with_prepared_proof(secret, cache.message, std::move(prepared), secp_context);
581}
582
583Result<ProvenSignature> sign_with_topic_proof(const SecretKey& secret, std::span<const unsigned char> message,
584 std::span<const unsigned char> topic,
585 purify_secp_context* secp_context,
586 bppp::ExperimentalCircuitBackend* circuit_cache) {
587 PURIFY_ASSIGN_OR_RETURN(auto prepared, prepare_topic_nonce_with_proof(secret, topic, secp_context, circuit_cache),
588 "sign_with_topic_proof:prepare_topic_nonce_with_proof");
589 return sign_with_prepared_topic_proof(secret, message, std::move(prepared), secp_context);
590}
591
592Result<ProvenSignature> sign_with_topic_proof(const SecretKey& secret, std::span<const unsigned char> message,
593 const TopicProofCache& cache,
594 purify_secp_context* secp_context,
595 bppp::ExperimentalCircuitBackend* circuit_cache) {
596 PURIFY_ASSIGN_OR_RETURN(auto prepared, prepare_topic_nonce_with_proof(secret, cache, secp_context, circuit_cache),
597 "sign_with_topic_proof:prepare_topic_nonce_with_proof");
598 return sign_with_prepared_topic_proof(secret, message, std::move(prepared), secp_context);
599}
600
601Result<bool> verify_signature(const PublicKey& public_key, std::span<const unsigned char> message,
602 const Signature& signature, purify_secp_context* secp_context) {
603 PURIFY_RETURN_IF_ERROR(validate_public_key(public_key.purify_pubkey), "verify_signature:validate_public_key");
604 PURIFY_RETURN_IF_ERROR(require_secp_context(secp_context, "verify_signature:secp_context"),
605 "verify_signature:secp_context");
606 if (purify_bip340_validate_xonly_pubkey(secp_context, public_key.bip340_pubkey.data()) == 0) {
607 return unexpected_error(ErrorCode::BackendRejectedInput, "verify_signature:bip340_validate_xonly_pubkey");
608 }
609 if (purify_bip340_validate_signature(secp_context, signature.bytes.data()) == 0) {
610 return unexpected_error(ErrorCode::BackendRejectedInput, "verify_signature:bip340_validate_signature");
611 }
612 return purify_bip340_verify(secp_context, signature.bytes.data(), nullable_data(message), message.size(),
613 public_key.bip340_pubkey.data()) != 0;
614}
615
616Result<bool> verify_message_nonce_proof(const PublicKey& public_key, std::span<const unsigned char> message,
617 const NonceProof& nonce_proof,
618 purify_secp_context* secp_context,
619 bppp::ExperimentalCircuitBackend* circuit_cache) {
620 PURIFY_ASSIGN_OR_RETURN(const auto& circuit,
621 verifier_circuit(detail::tagged_eval_input(kMessageNonceTag, message), public_key.purify_pubkey),
622 "verify_message_nonce_proof:verifier_circuit");
623 return verify_nonce_proof_with_circuit(public_key, circuit, nonce_proof, Scope::Message, secp_context,
624 "verify_message_nonce_proof:verify_nonce_proof_with_circuit",
625 circuit_cache);
626}
627
629 const NonceProof& nonce_proof,
630 purify_secp_context* secp_context,
631 bppp::ExperimentalCircuitBackend* circuit_cache) {
632 PURIFY_RETURN_IF_ERROR(validate_message_proof_cache(cache), "verify_message_nonce_proof:validate_message_proof_cache");
633 PURIFY_ASSIGN_OR_RETURN(const auto& circuit, cache.circuit_template.instantiate(public_key.purify_pubkey),
634 "verify_message_nonce_proof:instantiate");
635 return verify_nonce_proof_with_circuit(public_key, circuit, nonce_proof, Scope::Message, secp_context,
636 "verify_message_nonce_proof:verify_nonce_proof_with_circuit",
637 circuit_cache != nullptr ? circuit_cache : &cache.backend_cache);
638}
639
640Result<bool> verify_topic_nonce_proof(const PublicKey& public_key, std::span<const unsigned char> topic,
641 const NonceProof& nonce_proof,
642 purify_secp_context* secp_context,
643 bppp::ExperimentalCircuitBackend* circuit_cache) {
644 if (topic.empty()) {
645 return unexpected_error(ErrorCode::EmptyInput, "verify_topic_nonce_proof:empty_topic");
646 }
647 PURIFY_ASSIGN_OR_RETURN(const auto& circuit,
648 verifier_circuit(detail::tagged_eval_input(kTopicNonceTag, topic), public_key.purify_pubkey),
649 "verify_topic_nonce_proof:verifier_circuit");
650 return verify_nonce_proof_with_circuit(public_key, circuit, nonce_proof, Scope::Topic, secp_context,
651 "verify_topic_nonce_proof:verify_nonce_proof_with_circuit",
652 circuit_cache);
653}
654
656 const NonceProof& nonce_proof,
657 purify_secp_context* secp_context,
658 bppp::ExperimentalCircuitBackend* circuit_cache) {
659 PURIFY_RETURN_IF_ERROR(validate_topic_proof_cache(cache), "verify_topic_nonce_proof:validate_topic_proof_cache");
660 PURIFY_ASSIGN_OR_RETURN(const auto& circuit, cache.circuit_template.instantiate(public_key.purify_pubkey),
661 "verify_topic_nonce_proof:instantiate");
662 return verify_nonce_proof_with_circuit(public_key, circuit, nonce_proof, Scope::Topic, secp_context,
663 "verify_topic_nonce_proof:verify_nonce_proof_with_circuit",
664 circuit_cache != nullptr ? circuit_cache : &cache.backend_cache);
665}
666
668 std::span<const unsigned char> message,
669 const ProvenSignature& signature,
670 purify_secp_context* secp_context,
671 bppp::ExperimentalCircuitBackend* circuit_cache) {
672 PURIFY_ASSIGN_OR_RETURN(auto sig_ok, verify_signature(public_key, message, signature.signature, secp_context),
673 "verify_message_signature_with_proof:verify_signature");
674 if (!sig_ok) {
675 return false;
676 }
677 if (signature.signature.nonce().xonly != signature.nonce_proof.nonce.xonly) {
678 return false;
679 }
680 return verify_message_nonce_proof(public_key, message, signature.nonce_proof, secp_context, circuit_cache);
681}
682
684 const ProvenSignature& signature,
685 purify_secp_context* secp_context,
686 bppp::ExperimentalCircuitBackend* circuit_cache) {
687 PURIFY_ASSIGN_OR_RETURN(auto sig_ok, verify_signature(public_key, cache.message, signature.signature, secp_context),
688 "verify_message_signature_with_proof:verify_signature");
689 if (!sig_ok) {
690 return false;
691 }
692 if (signature.signature.nonce().xonly != signature.nonce_proof.nonce.xonly) {
693 return false;
694 }
695 return verify_message_nonce_proof(cache, public_key, signature.nonce_proof, secp_context, circuit_cache);
696}
697
699 std::span<const unsigned char> message,
700 std::span<const unsigned char> topic,
701 const ProvenSignature& signature,
702 purify_secp_context* secp_context,
703 bppp::ExperimentalCircuitBackend* circuit_cache) {
704 PURIFY_ASSIGN_OR_RETURN(auto sig_ok, verify_signature(public_key, message, signature.signature, secp_context),
705 "verify_topic_signature_with_proof:verify_signature");
706 if (!sig_ok) {
707 return false;
708 }
709 if (signature.signature.nonce().xonly != signature.nonce_proof.nonce.xonly) {
710 return false;
711 }
712 return verify_topic_nonce_proof(public_key, topic, signature.nonce_proof, secp_context, circuit_cache);
713}
714
716 std::span<const unsigned char> message,
717 const ProvenSignature& signature,
718 purify_secp_context* secp_context,
719 bppp::ExperimentalCircuitBackend* circuit_cache) {
720 PURIFY_ASSIGN_OR_RETURN(auto sig_ok, verify_signature(public_key, message, signature.signature, secp_context),
721 "verify_topic_signature_with_proof:verify_signature");
722 if (!sig_ok) {
723 return false;
724 }
725 if (signature.signature.nonce().xonly != signature.nonce_proof.nonce.xonly) {
726 return false;
727 }
728 return verify_topic_nonce_proof(cache, public_key, signature.nonce_proof, secp_context, circuit_cache);
729}
730
731} // namespace api_impl
732
734 return api_impl::derive_public_key(secret, secp_context);
735}
736
737Result<bool> PublicKey::verify_signature(std::span<const unsigned char> message, const Signature& signature,
738 purify_secp_context* secp_context) const {
739 return api_impl::verify_signature(*this, message, signature, secp_context);
740}
741
742Result<bool> PublicKey::verify_message_nonce_proof(std::span<const unsigned char> message,
743 const NonceProof& nonce_proof,
744 purify_secp_context* secp_context,
745 bppp::ExperimentalCircuitBackend* circuit_cache) const {
746 return api_impl::verify_message_nonce_proof(*this, message, nonce_proof, secp_context, circuit_cache);
747}
748
750 purify_secp_context* secp_context,
751 bppp::ExperimentalCircuitBackend* circuit_cache) const {
752 return api_impl::verify_message_nonce_proof(cache, *this, nonce_proof, secp_context, circuit_cache);
753}
754
755Result<bool> PublicKey::verify_topic_nonce_proof(std::span<const unsigned char> topic,
756 const NonceProof& nonce_proof,
757 purify_secp_context* secp_context,
758 bppp::ExperimentalCircuitBackend* circuit_cache) const {
759 return api_impl::verify_topic_nonce_proof(*this, topic, nonce_proof, secp_context, circuit_cache);
760}
761
763 purify_secp_context* secp_context,
764 bppp::ExperimentalCircuitBackend* circuit_cache) const {
765 return api_impl::verify_topic_nonce_proof(cache, *this, nonce_proof, secp_context, circuit_cache);
766}
767
769 const ProvenSignature& signature,
770 purify_secp_context* secp_context,
771 bppp::ExperimentalCircuitBackend* circuit_cache) const {
772 return api_impl::verify_message_signature_with_proof(*this, message, signature, secp_context, circuit_cache);
773}
774
776 const ProvenSignature& signature,
777 purify_secp_context* secp_context,
778 bppp::ExperimentalCircuitBackend* circuit_cache) const {
779 return api_impl::verify_message_signature_with_proof(cache, *this, signature, secp_context, circuit_cache);
780}
781
782Result<bool> PublicKey::verify_topic_signature_with_proof(std::span<const unsigned char> message,
783 std::span<const unsigned char> topic,
784 const ProvenSignature& signature,
785 purify_secp_context* secp_context,
786 bppp::ExperimentalCircuitBackend* circuit_cache) const {
787 return api_impl::verify_topic_signature_with_proof(*this, message, topic, signature, secp_context, circuit_cache);
788}
789
791 std::span<const unsigned char> message,
792 const ProvenSignature& signature,
793 purify_secp_context* secp_context,
794 bppp::ExperimentalCircuitBackend* circuit_cache) const {
795 return api_impl::verify_topic_signature_with_proof(cache, *this, message, signature, secp_context, circuit_cache);
796}
797
799 PURIFY_ASSIGN_OR_RETURN(auto owned_secret, secret.clone(), "KeyPair::from_secret:clone");
800 return KeyPair::from_secret(std::move(owned_secret), secp_context);
801}
802
804 PURIFY_ASSIGN_OR_RETURN(auto public_key, PublicKey::from_secret(secret, secp_context), "KeyPair::from_secret:from_secret");
805 return KeyPair(std::move(secret), std::move(public_key));
806}
807
808Result<PreparedNonce> KeyPair::prepare_message_nonce(std::span<const unsigned char> message,
809 purify_secp_context* secp_context) const {
810 return api_impl::prepare_message_nonce(secret_, message, secp_context);
811}
812
814 std::span<const unsigned char> message,
815 purify_secp_context* secp_context,
816 bppp::ExperimentalCircuitBackend* circuit_cache) const {
817 return api_impl::prepare_message_nonce_with_proof(secret_, message, secp_context, circuit_cache);
818}
819
821 const MessageProofCache& cache,
822 purify_secp_context* secp_context,
823 bppp::ExperimentalCircuitBackend* circuit_cache) const {
824 return api_impl::prepare_message_nonce_with_proof(secret_, cache, secp_context, circuit_cache);
825}
826
827Result<PreparedNonce> KeyPair::prepare_topic_nonce(std::span<const unsigned char> topic,
828 purify_secp_context* secp_context) const {
829 return api_impl::prepare_topic_nonce(secret_, topic, secp_context);
830}
831
833 std::span<const unsigned char> topic,
834 purify_secp_context* secp_context,
835 bppp::ExperimentalCircuitBackend* circuit_cache) const {
836 return api_impl::prepare_topic_nonce_with_proof(secret_, topic, secp_context, circuit_cache);
837}
838
840 const TopicProofCache& cache,
841 purify_secp_context* secp_context,
842 bppp::ExperimentalCircuitBackend* circuit_cache) const {
843 return api_impl::prepare_topic_nonce_with_proof(secret_, cache, secp_context, circuit_cache);
844}
845
846Result<Signature> KeyPair::sign_message(std::span<const unsigned char> message,
847 purify_secp_context* secp_context) const {
848 PURIFY_ASSIGN_OR_RETURN(auto prepared, prepare_message_nonce(message, secp_context), "KeyPair::sign_message:prepare_message_nonce");
849 return sign_message_with_prepared(message, std::move(prepared), secp_context);
850}
851
852Result<Signature> KeyPair::sign_message_with_prepared(std::span<const unsigned char> message,
853 PreparedNonce&& prepared,
854 purify_secp_context* secp_context) const {
855 PURIFY_ASSIGN_OR_RETURN(const auto& signer, derive_bip340_key(secret_, secp_context), "KeyPair::sign_message_with_prepared:derive_bip340_key");
856 return std::move(prepared).sign_message(signer, message, secp_context);
857}
858
860 PreparedNonceWithProof&& prepared,
861 purify_secp_context* secp_context) const {
862 return std::move(prepared).sign_message(secret_, message, secp_context);
863}
864
865Result<Signature> KeyPair::sign_with_topic(std::span<const unsigned char> message,
866 std::span<const unsigned char> topic,
867 purify_secp_context* secp_context) const {
868 PURIFY_ASSIGN_OR_RETURN(auto prepared, prepare_topic_nonce(topic, secp_context), "KeyPair::sign_with_topic:prepare_topic_nonce");
869 return sign_with_prepared_topic(message, std::move(prepared), secp_context);
870}
871
872Result<Signature> KeyPair::sign_with_prepared_topic(std::span<const unsigned char> message,
873 PreparedNonce&& prepared,
874 purify_secp_context* secp_context) const {
875 PURIFY_ASSIGN_OR_RETURN(const auto& signer, derive_bip340_key(secret_, secp_context), "KeyPair::sign_with_prepared_topic:derive_bip340_key");
876 return std::move(prepared).sign_topic_message(signer, message, secp_context);
877}
878
880 PreparedNonceWithProof&& prepared,
881 purify_secp_context* secp_context) const {
882 return std::move(prepared).sign_topic_message(secret_, message, secp_context);
883}
884
885Result<ProvenSignature> KeyPair::sign_message_with_proof(std::span<const unsigned char> message,
886 purify_secp_context* secp_context,
887 bppp::ExperimentalCircuitBackend* circuit_cache) const {
888 PURIFY_ASSIGN_OR_RETURN(auto prepared, prepare_message_nonce_with_proof(message, secp_context, circuit_cache),
889 "KeyPair::sign_message_with_proof:prepare_message_nonce_with_proof");
890 return sign_message_with_prepared_proof(message, std::move(prepared), secp_context);
891}
892
894 purify_secp_context* secp_context,
895 bppp::ExperimentalCircuitBackend* circuit_cache) const {
896 PURIFY_ASSIGN_OR_RETURN(auto prepared, prepare_message_nonce_with_proof(cache, secp_context, circuit_cache),
897 "KeyPair::sign_message_with_proof:prepare_message_nonce_with_proof");
898 return sign_message_with_prepared_proof(cache.message, std::move(prepared), secp_context);
899}
900
901Result<ProvenSignature> KeyPair::sign_with_topic_proof(std::span<const unsigned char> message,
902 std::span<const unsigned char> topic,
903 purify_secp_context* secp_context,
904 bppp::ExperimentalCircuitBackend* circuit_cache) const {
905 PURIFY_ASSIGN_OR_RETURN(auto prepared, prepare_topic_nonce_with_proof(topic, secp_context, circuit_cache),
906 "KeyPair::sign_with_topic_proof:prepare_topic_nonce_with_proof");
907 return sign_with_prepared_topic_proof(message, std::move(prepared), secp_context);
908}
909
910Result<ProvenSignature> KeyPair::sign_with_topic_proof(std::span<const unsigned char> message,
911 const TopicProofCache& cache,
912 purify_secp_context* secp_context,
913 bppp::ExperimentalCircuitBackend* circuit_cache) const {
914 PURIFY_ASSIGN_OR_RETURN(auto prepared, prepare_topic_nonce_with_proof(cache, secp_context, circuit_cache),
915 "KeyPair::sign_with_topic_proof:prepare_topic_nonce_with_proof");
916 return sign_with_prepared_topic_proof(message, std::move(prepared), secp_context);
917}
918
919} // namespace purify::puresign_plusplus
Purify result carrier that either holds a value or an error.
Definition expected.hpp:64
Result< NativeBulletproofCircuit > instantiate(const UInt512 &pubkey) const
Result< Bytes > integrity_digest() const
Returns a stable digest of the packed base circuit plus late-bound expressions.
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
Common interface for reusable experimental BPPP backend state.
Definition bppp.hpp:119
Result< PreparedNonce > prepare_message_nonce(std::span< const unsigned char > message, purify_secp_context *secp_context) const
Deterministically prepares a message-bound nonce.
Definition bppp.cpp:808
Result< PreparedNonce > prepare_topic_nonce(std::span< const unsigned char > topic, purify_secp_context *secp_context) const
Deterministically prepares a topic-bound nonce.
Definition bppp.cpp:827
Result< ProvenSignature > sign_message_with_proof(std::span< const unsigned char > message, purify_secp_context *secp_context, bppp::ExperimentalCircuitBackend *circuit_cache=nullptr) const
Signs a message and returns the signature bundled with its BPPP nonce proof.
Definition bppp.cpp:885
const PublicKey & public_key() const noexcept
Returns the public key bundle associated with this signer.
Definition bppp.hpp:458
Result< Signature > sign_with_prepared_topic(std::span< const unsigned char > message, PreparedNonce &&prepared, purify_secp_context *secp_context) const
Signs a message using an already prepared topic-bound nonce.
Definition bppp.cpp:872
static Result< KeyPair > from_secret(const SecretKey &secret, purify_secp_context *secp_context)
Derives a PureSign++ signing key pair from one packed Purify secret.
Definition bppp.cpp:798
Result< PreparedNonceWithProof > prepare_message_nonce_with_proof(std::span< const unsigned char > message, purify_secp_context *secp_context, bppp::ExperimentalCircuitBackend *circuit_cache=nullptr) const
Deterministically prepares a message-bound nonce together with its BPPP proof.
Definition bppp.cpp:813
Result< ProvenSignature > sign_with_prepared_topic_proof(std::span< const unsigned char > message, PreparedNonceWithProof &&prepared, purify_secp_context *secp_context) const
Signs a message using an already prepared topic-bound nonce proof bundle.
Definition bppp.cpp:879
Result< Signature > sign_message_with_prepared(std::span< const unsigned char > message, PreparedNonce &&prepared, purify_secp_context *secp_context) const
Signs a message using an already prepared message-bound nonce.
Definition bppp.cpp:852
Result< ProvenSignature > sign_with_topic_proof(std::span< const unsigned char > message, std::span< const unsigned char > topic, purify_secp_context *secp_context, bppp::ExperimentalCircuitBackend *circuit_cache=nullptr) const
Signs a message with a topic-bound nonce proof.
Definition bppp.cpp:901
Result< Signature > sign_message(std::span< const unsigned char > message, purify_secp_context *secp_context) const
Signs a message with a deterministically derived message-bound nonce.
Definition bppp.cpp:846
Result< PreparedNonceWithProof > prepare_topic_nonce_with_proof(std::span< const unsigned char > topic, purify_secp_context *secp_context, bppp::ExperimentalCircuitBackend *circuit_cache=nullptr) const
Deterministically prepares a topic-bound nonce together with its BPPP proof.
Definition bppp.cpp:832
Result< Signature > sign_with_topic(std::span< const unsigned char > message, std::span< const unsigned char > topic, purify_secp_context *secp_context) const
Signs a message using a topic-bound deterministic nonce.
Definition bppp.cpp:865
Result< ProvenSignature > sign_message_with_prepared_proof(std::span< const unsigned char > message, PreparedNonceWithProof &&prepared, purify_secp_context *secp_context) const
Signs a message using an already prepared message-bound nonce proof bundle.
Definition bppp.cpp:859
const NonceProof & proof() const noexcept
Returns the public nonce proof carried by this bundle.
Definition bppp.hpp:382
Result< ProvenSignature > sign_message(const SecretKey &secret, std::span< const unsigned char > message, purify_secp_context *secp_context) &&
Consumes this message-bound prepared proof bundle and signs the message.
Definition bppp.cpp:400
static PreparedNonceWithProof from_parts(PreparedNonce prepared, NonceProof proof)
Bundles a prepared nonce with its matching BPPP-backed public nonce proof.
Definition bppp.cpp:396
Result< ProvenSignature > sign_topic_message(const SecretKey &secret, std::span< const unsigned char > message, purify_secp_context *secp_context) &&
Consumes this topic-bound prepared proof bundle and signs the message.
Definition bppp.cpp:413
Move-only prepared nonce bound to either a message or a topic.
Definition bppp.hpp:284
Result< Signature > sign_message(const Bip340Key &signer, std::span< const unsigned char > message, purify_secp_context *secp_context) &&
Consumes this message-bound nonce and signs the matching message.
Definition bppp.cpp:338
const Nonce & public_nonce() const noexcept
Returns the public nonce corresponding to this prepared secret nonce scalar.
Definition bppp.hpp:302
PreparedNonce & operator=(const PreparedNonce &)=delete
PreparedNonce(const PreparedNonce &)=delete
Result< Signature > sign_topic_message(const Bip340Key &signer, std::span< const unsigned char > message, purify_secp_context *secp_context) &&
Consumes this topic-bound nonce and signs a message under that topic binding.
Definition bppp.cpp:368
Scalar32 scalar() const
Explicitly exports the secret nonce scalar.
Definition bppp.hpp:312
static PreparedNonce from_parts(Scope scope, const Scalar32 &scalar, const Nonce &nonce, const XOnly32 &signer_pubkey, const XOnly32 &binding_digest)
Builds a prepared nonce from already-derived nonce components.
Definition bppp.cpp:306
#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
GeneratorBytes base_generator(purify_secp_context *secp_context)
Returns the serialized secp256k1 base generator used as the blind generator.
Definition bppp.cpp:1064
Result< bool > verify_experimental_circuit_zk_norm_arg_with_public_commitments(const NativeBulletproofCircuit &circuit, const ExperimentalCircuitZkNormArgProof &proof, std::span< const PointBytes > public_commitments, purify_secp_context *secp_context, std::span< const unsigned char > statement_binding={}, ExperimentalCircuitBackend *cache=nullptr)
Verifies an experimental masked circuit proof against explicit public commitment points.
Definition bppp.cpp:1770
Result< ExperimentalCircuitZkNormArgProof > prove_experimental_circuit_zk_norm_arg_with_public_commitments(const NativeBulletproofCircuit &circuit, const BulletproofAssignmentData &assignment, const ScalarBytes &nonce, std::span< const PointBytes > public_commitments, purify_secp_context *secp_context, std::span< const unsigned char > statement_binding={}, ExperimentalCircuitBackend *cache=nullptr)
Produces an experimental masked circuit proof bound to explicit public commitment points.
Definition bppp.cpp:1758
Result< PointBytes > pedersen_commit_char(const ScalarBytes &blind, const ScalarBytes &value, purify_secp_context *secp_context)
Computes a Pedersen commitment to an arbitrary 32-byte scalar value using Purify's default generators...
Definition bppp.cpp:1155
ScalarBytes scalar_bytes(const FieldElement &value)
Serializes a Purify field element into the scalar encoding expected by the BPPP bridge.
Definition bppp.hpp:78
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
Bytes copy_bytes(std::span< const unsigned char > input)
Definition common.hpp:17
Status validate_proof_cache_circuit(const CircuitLike &circuit, const char *context)
Definition common.hpp:64
Result< PublicKey > derive_public_key(const SecretKey &secret, purify_secp_context *secp_context)
Definition bppp.cpp:428
Result< PreparedNonce > prepare_message_nonce(const SecretKey &secret, std::span< const unsigned char > message, purify_secp_context *secp_context)
Definition bppp.cpp:442
Result< Signature > sign_message_with_prepared(const SecretKey &secret, std::span< const unsigned char > message, PreparedNonce &&prepared, purify_secp_context *secp_context)
Definition bppp.cpp:534
Result< bool > verify_topic_nonce_proof(const PublicKey &public_key, std::span< const unsigned char > topic, const NonceProof &nonce_proof, purify_secp_context *secp_context, bppp::ExperimentalCircuitBackend *circuit_cache)
Definition bppp.cpp:640
Result< PreparedNonce > prepare_topic_nonce(const SecretKey &secret, std::span< const unsigned char > topic, purify_secp_context *secp_context)
Definition bppp.cpp:485
Result< PreparedNonceWithProof > prepare_message_nonce_with_proof(const SecretKey &secret, std::span< const unsigned char > message, purify_secp_context *secp_context, bppp::ExperimentalCircuitBackend *circuit_cache)
Definition bppp.cpp:450
Result< ProvenSignature > sign_message_with_proof(const SecretKey &secret, std::span< const unsigned char > message, purify_secp_context *secp_context, bppp::ExperimentalCircuitBackend *circuit_cache)
Definition bppp.cpp:567
Result< Signature > sign_with_prepared_topic(const SecretKey &secret, std::span< const unsigned char > message, PreparedNonce &&prepared, purify_secp_context *secp_context)
Definition bppp.cpp:554
Result< bool > verify_message_nonce_proof(const PublicKey &public_key, std::span< const unsigned char > message, const NonceProof &nonce_proof, purify_secp_context *secp_context, bppp::ExperimentalCircuitBackend *circuit_cache)
Definition bppp.cpp:616
Result< ProvenSignature > sign_message_with_prepared_proof(const SecretKey &secret, std::span< const unsigned char > message, PreparedNonceWithProof &&prepared, purify_secp_context *secp_context)
Definition bppp.cpp:540
Result< ProvenSignature > sign_with_topic_proof(const SecretKey &secret, std::span< const unsigned char > message, std::span< const unsigned char > topic, purify_secp_context *secp_context, bppp::ExperimentalCircuitBackend *circuit_cache)
Definition bppp.cpp:583
Result< MessageProofCache > build_message_proof_cache(std::span< const unsigned char > message)
Definition bppp.cpp:434
Result< bool > verify_topic_signature_with_proof(const PublicKey &public_key, std::span< const unsigned char > message, std::span< const unsigned char > topic, const ProvenSignature &signature, purify_secp_context *secp_context, bppp::ExperimentalCircuitBackend *circuit_cache)
Definition bppp.cpp:698
Result< PreparedNonceWithProof > prepare_topic_nonce_with_proof(const SecretKey &secret, std::span< const unsigned char > topic, purify_secp_context *secp_context, bppp::ExperimentalCircuitBackend *circuit_cache)
Definition bppp.cpp:493
Result< Signature > sign_with_topic(const SecretKey &secret, std::span< const unsigned char > message, std::span< const unsigned char > topic, purify_secp_context *secp_context)
Definition bppp.cpp:547
Result< ProvenSignature > sign_with_prepared_topic_proof(const SecretKey &secret, std::span< const unsigned char > message, PreparedNonceWithProof &&prepared, purify_secp_context *secp_context)
Definition bppp.cpp:560
Result< bool > verify_signature(const PublicKey &public_key, std::span< const unsigned char > message, const Signature &signature, purify_secp_context *secp_context)
Definition bppp.cpp:601
Result< Signature > sign_message(const SecretKey &secret, std::span< const unsigned char > message, purify_secp_context *secp_context)
Definition bppp.cpp:528
Result< TopicProofCache > build_topic_proof_cache(std::span< const unsigned char > topic)
Definition bppp.cpp:438
Result< bool > verify_message_signature_with_proof(const PublicKey &public_key, std::span< const unsigned char > message, const ProvenSignature &signature, purify_secp_context *secp_context, bppp::ExperimentalCircuitBackend *circuit_cache)
Definition bppp.cpp:667
std::array< unsigned char, 32 > Scalar32
Definition bppp.hpp:22
std::array< unsigned char, 32 > XOnly32
Definition bppp.hpp:23
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< 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
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
Experimental BPPP-backed PureSign proof(R) helpers.
Narrow C ABI exposing secp256k1 scalar and HMAC helpers to the C++ headers.
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 for the BPPP-backed PureSign++ proof(R) flow.
Definition bppp.hpp:218
bppp::ExperimentalCircuitCache backend_cache
Definition bppp.hpp:223
NativeBulletproofCircuitTemplate circuit_template
Definition bppp.hpp:221
static Result< MessageProofCache > build(std::span< const unsigned char > message)
Builds a reusable verifier template for one exact message.
Definition bppp.cpp:271
Public BIP340 nonce point in x-only form.
Definition bppp.hpp:182
static Result< PublicKey > from_secret(const SecretKey &secret, purify_secp_context *secp_context)
Derives both public identities from one packed Purify secret.
Definition bppp.cpp:733
Result< bool > verify_message_nonce_proof(std::span< const unsigned char > message, const NonceProof &nonce_proof, purify_secp_context *secp_context, bppp::ExperimentalCircuitBackend *circuit_cache=nullptr) const
Verifies a message-bound BPPP nonce proof against this public key.
Definition bppp.cpp:742
Result< bool > verify_topic_nonce_proof(std::span< const unsigned char > topic, const NonceProof &nonce_proof, purify_secp_context *secp_context, bppp::ExperimentalCircuitBackend *circuit_cache=nullptr) const
Verifies a topic-bound BPPP nonce proof against this public key.
Definition bppp.cpp:755
Result< bool > verify_signature(std::span< const unsigned char > message, const Signature &signature, purify_secp_context *secp_context) const
Verifies a plain BIP340 signature against this bundle's x-only public key.
Definition bppp.cpp:737
Result< bool > verify_message_signature_with_proof(std::span< const unsigned char > message, const ProvenSignature &signature, purify_secp_context *secp_context, bppp::ExperimentalCircuitBackend *circuit_cache=nullptr) const
Verifies a message signature bundled with its BPPP nonce proof.
Definition bppp.cpp:768
Result< bool > verify_topic_signature_with_proof(std::span< const unsigned char > message, std::span< const unsigned char > topic, const ProvenSignature &signature, purify_secp_context *secp_context, bppp::ExperimentalCircuitBackend *circuit_cache=nullptr) const
Verifies a topic-bound signature bundled with its BPPP nonce proof.
Definition bppp.cpp:782
Standard 64-byte BIP340 signature.
Definition bppp.hpp:195
Nonce nonce() const
Returns the x-only public nonce encoded in the first 32 signature bytes.
Definition bppp.cpp:86
Cacheable topic-bound nonce-proof template for the BPPP-backed PureSign++ proof(R) flow.
Definition bppp.hpp:240
bppp::ExperimentalCircuitCache backend_cache
Definition bppp.hpp:245
NativeBulletproofCircuitTemplate circuit_template
Definition bppp.hpp:243
static Result< TopicProofCache > build(std::span< const unsigned char > topic)
Builds a reusable verifier template for one exact topic.
Definition bppp.cpp:285