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 <cassert>
14#include <cstdint>
15#include <limits>
16#include <optional>
17
18#include "../detail/common.hpp"
19#include "purify/secp_bridge.h"
20
21namespace purify::puresign {
22
23namespace {
24
25Result<bool> nonce_proof_matches_nonce(const NonceProof& nonce_proof, purify_secp_context* secp_context) {
26 XOnly32 xonly{};
27 int parity = 0;
28 PURIFY_RETURN_IF_ERROR(require_secp_context(secp_context, "nonce_proof_matches_nonce:secp_context"),
29 "nonce_proof_matches_nonce:secp_context");
30 if (purify_bip340_xonly_from_point(secp_context, nonce_proof.proof.commitment.data(), xonly.data(), &parity) == 0) {
31 return unexpected_error(ErrorCode::BackendRejectedInput, "nonce_proof_matches_nonce:invalid_commitment");
32 }
33 (void)parity;
34 return xonly == nonce_proof.nonce.xonly;
35}
36
37} // namespace
38
40 Bytes out;
41 out.reserve(kSerializedSize);
42 std::array<unsigned char, 64> packed = purify_pubkey.to_bytes_be();
43 out.insert(out.end(), packed.begin(), packed.end());
44 out.insert(out.end(), bip340_pubkey.begin(), bip340_pubkey.end());
45 return out;
46}
47
48Result<PublicKey> PublicKey::deserialize(std::span<const unsigned char> serialized,
49 purify_secp_context* secp_context) {
50 if (serialized.size() != kSerializedSize) {
51 return unexpected_error(ErrorCode::InvalidFixedSize, "PublicKey::deserialize:size");
52 }
53 UInt512 purify_pubkey = UInt512::from_bytes_be(serialized.data(), 64);
54 PURIFY_RETURN_IF_ERROR(validate_public_key(purify_pubkey), "PublicKey::deserialize:validate_public_key");
55
56 PublicKey out{};
58 std::copy(serialized.begin() + 64, serialized.end(), out.bip340_pubkey.begin());
59 PURIFY_RETURN_IF_ERROR(require_secp_context(secp_context, "PublicKey::deserialize:secp_context"),
60 "PublicKey::deserialize:secp_context");
61 if (purify_bip340_validate_xonly_pubkey(secp_context, out.bip340_pubkey.data()) == 0) {
62 return unexpected_error(ErrorCode::BackendRejectedInput, "PublicKey::deserialize:bip340_validate_xonly_pubkey");
63 }
64 return out;
65}
66
68 return Bytes(xonly.begin(), xonly.end());
69}
70
71Result<Nonce> Nonce::deserialize(std::span<const unsigned char> serialized,
72 purify_secp_context* secp_context) {
73 if (serialized.size() != kSerializedSize) {
74 return unexpected_error(ErrorCode::InvalidFixedSize, "Nonce::deserialize:size");
75 }
76 Nonce out{};
77 std::copy(serialized.begin(), serialized.end(), out.xonly.begin());
78 PURIFY_RETURN_IF_ERROR(require_secp_context(secp_context, "Nonce::deserialize:secp_context"),
79 "Nonce::deserialize:secp_context");
80 if (purify_bip340_validate_xonly_pubkey(secp_context, out.xonly.data()) == 0) {
81 return unexpected_error(ErrorCode::BackendRejectedInput, "Nonce::deserialize:bip340_validate_xonly_pubkey");
82 }
83 return out;
84}
85
87 Nonce out{};
88 std::copy(bytes.begin(), bytes.begin() + 32, out.xonly.begin());
89 return out;
90}
91
93 Scalar32 out{};
94 std::copy(bytes.begin() + 32, bytes.end(), out.begin());
95 return out;
96}
97
99 return Bytes(bytes.begin(), bytes.end());
100}
101
102Result<Signature> Signature::deserialize(std::span<const unsigned char> serialized,
103 purify_secp_context* secp_context) {
104 if (serialized.size() != kSerializedSize) {
105 return unexpected_error(ErrorCode::InvalidFixedSize, "Signature::deserialize:size");
106 }
107 Signature out{};
108 std::copy(serialized.begin(), serialized.end(), out.bytes.begin());
109 PURIFY_RETURN_IF_ERROR(require_secp_context(secp_context, "Signature::deserialize:secp_context"),
110 "Signature::deserialize:secp_context");
111 if (purify_bip340_validate_signature(secp_context, out.bytes.data()) == 0) {
112 return unexpected_error(ErrorCode::BackendRejectedInput, "Signature::deserialize:bip340_validate_signature");
113 }
114 return out;
115}
116
118 PURIFY_ASSIGN_OR_RETURN(auto match, nonce_proof_matches_nonce(*this, secp_context),
119 "NonceProof::serialize:nonce_proof_matches_nonce");
120 if (!match) {
121 return unexpected_error(ErrorCode::BindingMismatch, "NonceProof::serialize:nonce_mismatch");
122 }
123 PURIFY_ASSIGN_OR_RETURN(const auto& proof_bytes, proof.serialize(), "NonceProof::serialize:proof");
124 if (proof_bytes.size() > static_cast<std::size_t>(std::numeric_limits<std::uint32_t>::max())) {
125 return unexpected_error(ErrorCode::UnexpectedSize, "NonceProof::serialize:proof_size");
126 }
127 std::size_t serialized_size = 0;
128 if (!checked_add_size(37, proof_bytes.size(), serialized_size)) {
129 return unexpected_error(ErrorCode::Overflow, "NonceProof::serialize:reserve");
130 }
131
132 Bytes out;
133 out.reserve(serialized_size);
134 out.push_back(static_cast<unsigned char>(2));
135 detail::append_u32_le(out, static_cast<std::uint32_t>(proof_bytes.size()));
136 out.insert(out.end(), nonce.xonly.begin(), nonce.xonly.end());
137 out.insert(out.end(), proof_bytes.begin(), proof_bytes.end());
138 return out;
139}
140
141Result<NonceProof> NonceProof::deserialize(std::span<const unsigned char> serialized,
142 purify_secp_context* secp_context) {
143 if (serialized.size() < 37) {
144 return unexpected_error(ErrorCode::InvalidFixedSize, "NonceProof::deserialize:header");
145 }
146 if (serialized[0] != 2) {
147 return unexpected_error(ErrorCode::BackendRejectedInput, "NonceProof::deserialize:version");
148 }
149 std::optional<std::uint32_t> proof_size = detail::read_u32_le(serialized, 1);
150 assert(proof_size.has_value() && "header length check should guarantee a u32 proof size");
151 if (*proof_size != serialized.size() - 37) {
152 return unexpected_error(ErrorCode::InvalidFixedSize, "NonceProof::deserialize:proof_size");
153 }
154
155 NonceProof out{};
156 std::copy_n(serialized.begin() + 5, 32, out.nonce.xonly.begin());
157 PURIFY_ASSIGN_OR_RETURN(auto proof_value,
158 ExperimentalBulletproofProof::deserialize(serialized.subspan(37, *proof_size)),
159 "NonceProof::deserialize:proof");
160 out.proof = std::move(proof_value);
161 PURIFY_ASSIGN_OR_RETURN(auto match, nonce_proof_matches_nonce(out, secp_context),
162 "NonceProof::deserialize:nonce_proof_matches_nonce");
163 if (!match) {
164 return unexpected_error(ErrorCode::BindingMismatch, "NonceProof::deserialize:nonce_mismatch");
165 }
166 return out;
167}
168
170 PURIFY_ASSIGN_OR_RETURN(const auto& nonce_proof_bytes, nonce_proof.serialize(secp_context),
171 "ProvenSignature::serialize:nonce_proof");
172 if (nonce_proof_bytes.size() > static_cast<std::size_t>(std::numeric_limits<std::uint32_t>::max())) {
173 return unexpected_error(ErrorCode::UnexpectedSize, "ProvenSignature::serialize:nonce_proof_size");
174 }
175 std::size_t serialized_size = 0;
176 if (!checked_add_size(69, nonce_proof_bytes.size(), serialized_size)) {
177 return unexpected_error(ErrorCode::Overflow, "ProvenSignature::serialize:reserve");
178 }
179
180 Bytes out;
181 out.reserve(serialized_size);
182 out.push_back(static_cast<unsigned char>(1));
183 detail::append_u32_le(out, static_cast<std::uint32_t>(nonce_proof_bytes.size()));
184 out.insert(out.end(), nonce_proof_bytes.begin(), nonce_proof_bytes.end());
185 out.insert(out.end(), signature.bytes.begin(), signature.bytes.end());
186 return out;
187}
188
189Result<ProvenSignature> ProvenSignature::deserialize(std::span<const unsigned char> serialized,
190 purify_secp_context* secp_context) {
191 if (serialized.size() < 69) {
192 return unexpected_error(ErrorCode::InvalidFixedSize, "ProvenSignature::deserialize:header");
193 }
194 if (serialized[0] != 1) {
195 return unexpected_error(ErrorCode::BackendRejectedInput, "ProvenSignature::deserialize:version");
196 }
197 std::optional<std::uint32_t> nonce_proof_size = detail::read_u32_le(serialized, 1);
198 assert(nonce_proof_size.has_value() && "header length check should guarantee a u32 nonce proof size");
199 const std::size_t payload_size = serialized.size() - 5;
200 if (*nonce_proof_size > payload_size || payload_size - *nonce_proof_size != 64) {
201 return unexpected_error(ErrorCode::InvalidFixedSize, "ProvenSignature::deserialize:size");
202 }
203
204 PURIFY_ASSIGN_OR_RETURN(auto nonce_proof_value,
205 NonceProof::deserialize(serialized.subspan(5, *nonce_proof_size), secp_context),
206 "ProvenSignature::deserialize:nonce_proof");
207 PURIFY_ASSIGN_OR_RETURN(auto signature_value,
208 Signature::deserialize(serialized.subspan(5 + *nonce_proof_size, 64), secp_context),
209 "ProvenSignature::deserialize:signature");
210 return ProvenSignature{signature_value, nonce_proof_value};
211}
212
213} // namespace purify::puresign
Purify result carrier that either holds a value or an error.
Definition expected.hpp:64
#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
Legacy Bulletproof-backed Purify-derived BIP340 signing helpers with prepared nonces.
std::optional< std::uint32_t > read_u32_le(std::span< const unsigned char > bytes, std::size_t offset)
Definition common.hpp:35
void append_u32_le(Bytes &out, std::uint32_t value)
Definition common.hpp:29
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
Status validate_public_key(const UInt512 &packed)
Validates the packed public-key encoding range.
Definition curve.cpp:314
std::vector< unsigned char > Bytes
Dynamically sized byte string used for messages, serialized witnesses, and proofs.
Definition common.hpp:99
bool checked_add_size(std::size_t lhs, std::size_t rhs, std::size_t &out) noexcept
Definition common.hpp:63
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_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.
static BigUInt from_bytes_be(const unsigned char *data, std::size_t size)
Parses a big-endian byte string into the fixed-width integer.
Definition numeric.hpp:235
std::array< unsigned char, Words *8 > to_bytes_be() const
Serializes the value to a fixed-width big-endian byte array.
Definition numeric.hpp:621
static Result< ExperimentalBulletproofProof > deserialize(std::span< const unsigned char > bytes)
Public nonce together with its experimental Purify statement proof.
Definition legacy.hpp:221
ExperimentalBulletproofProof proof
Definition legacy.hpp:223
static Result< NonceProof > deserialize(std::span< const unsigned char > serialized, purify_secp_context *secp_context)
Definition legacy.cpp:141
Result< Bytes > serialize(purify_secp_context *secp_context) const
Definition legacy.cpp:117
Public BIP340 nonce point in x-only form.
Definition legacy.hpp:190
static Result< Nonce > deserialize(std::span< const unsigned char > serialized, purify_secp_context *secp_context)
Definition legacy.cpp:71
static constexpr std::size_t kSerializedSize
Definition legacy.hpp:191
Bytes serialize() const
Definition legacy.cpp:67
Standard signature bundled with the public nonce proof it relied on.
Definition legacy.hpp:231
static Result< ProvenSignature > deserialize(std::span< const unsigned char > serialized, purify_secp_context *secp_context)
Definition legacy.cpp:189
Result< Bytes > serialize(purify_secp_context *secp_context) const
Definition legacy.cpp:169
Public key bundle pairing a Purify packed public key with its derived BIP340 x-only key.
Definition legacy.hpp:42
static constexpr std::size_t kSerializedSize
Definition legacy.hpp:43
Bytes serialize() const
Serializes this public-key bundle into its fixed-size wire format.
Definition legacy.cpp:39
static Result< PublicKey > deserialize(std::span< const unsigned char > serialized, purify_secp_context *secp_context)
Parses a serialized public-key bundle.
Definition legacy.cpp:48
Standard 64-byte BIP340 signature.
Definition legacy.hpp:201
static constexpr std::size_t kSerializedSize
Definition legacy.hpp:202
static Result< Signature > deserialize(std::span< const unsigned char > serialized, purify_secp_context *secp_context)
Definition legacy.cpp:102
Bytes serialize() const
Definition legacy.cpp:98
Scalar32 s() const
Definition legacy.cpp:92