Copying over pedersen commitment from crypto lib (#6238)

This commit is contained in:
Shawn Xie
2026-01-19 13:56:10 -05:00
committed by GitHub
parent 75d143a2a0
commit a5f20c129d
4 changed files with 373 additions and 47 deletions

View File

@@ -378,23 +378,6 @@ verifyClawbackEqualityProof(
return tesSUCCESS;
}
std::vector<Buffer>
getEqualityProofs(Slice const& zkp)
{
if (zkp.size() % ecEqualityProofLength != 0)
return {};
auto const count = zkp.size() / ecEqualityProofLength;
std::vector<Buffer> zkps;
zkps.reserve(count);
for (size_t i = 0; i < count; ++i)
zkps.emplace_back(
zkp.data() + (i * ecEqualityProofLength), ecEqualityProofLength);
return zkps;
}
NotTEC
checkEncryptedAmountFormat(STObject const& object)
{
@@ -419,6 +402,42 @@ checkEncryptedAmountFormat(STObject const& object)
return tesSUCCESS;
}
TER
verifyPedersenLinkage(
Slice const& proof,
Slice const& encAmt,
Slice const& pubKeySlice,
Slice const& pcmSlice,
uint256 const& contextHash)
{
if (proof.length() != ecPedersenProofLength)
return tecINTERNAL;
secp256k1_pubkey c1;
secp256k1_pubkey c2;
if (!makeEcPair(encAmt, c1, c2))
return tecINTERNAL;
secp256k1_pubkey pubKey;
std::memcpy(pubKey.data, pubKeySlice.data(), ecPubKeyLength);
secp256k1_pubkey pcm;
std::memcpy(pcm.data, pcmSlice.data(), ecPubKeyLength);
if (secp256k1_elgamal_pedersen_link_verify(
secp256k1Context(),
proof.data(),
&c1,
&c2,
&pubKey,
&pcm,
contextHash.data()) != 1)
return tecBAD_PROOF;
return tesSUCCESS;
}
// The following functions belong to the mpt-crypto library,
// they will be finally removed and we will use conan2 to manage the dependency.
int
@@ -1246,4 +1265,271 @@ secp256k1_elgamal_verify_encryption(
return 1; // Success: Encryption is valid
}
void
get_h_generator(secp256k1_context const* ctx, secp256k1_pubkey* h)
{
unsigned char h_scalar[32] = {0};
h_scalar[31] = 0x03;
if (!secp256k1_ec_pubkey_create(ctx, h, h_scalar))
{
fprintf(stderr, "ABORT: secp256k1_ec_pubkey_create failed\n");
}
}
void
build_link_challenge_hash(
secp256k1_context const* ctx,
unsigned char hash_input[290],
secp256k1_pubkey const* c1,
secp256k1_pubkey const* c2,
secp256k1_pubkey const* pk,
secp256k1_pubkey const* pcm,
secp256k1_pubkey const* T1,
secp256k1_pubkey const* T2,
secp256k1_pubkey const* T3,
unsigned char const* context_id)
{
char const* domain_sep = "MPT_ELGAMAL_PEDERSEN_LINK";
size_t offset = 0, len;
memset(hash_input, 0, 290);
memcpy(hash_input + offset, domain_sep, 25);
offset += 27;
secp256k1_pubkey const* points[] = {c1, c2, pk, pcm, T1, T2, T3};
for (int i = 0; i < 7; i++)
{
len = 33;
secp256k1_ec_pubkey_serialize(
ctx, hash_input + offset, &len, points[i], SECP256K1_EC_COMPRESSED);
offset += 33;
}
memcpy(hash_input + offset, context_id, 32);
}
int
secp256k1_elgamal_pedersen_link_prove(
secp256k1_context const* ctx,
unsigned char* proof,
secp256k1_pubkey const* c1,
secp256k1_pubkey const* c2,
secp256k1_pubkey const* pk,
secp256k1_pubkey const* pcm,
uint64_t amount,
unsigned char const* r,
unsigned char const* rho,
unsigned char const* context_id)
{
unsigned char km[32], kr[32], krho[32], e[32], sm[32], sr[32], srho[32],
m_sc[32] = {0};
secp256k1_pubkey T1, T2, T3, H, mG, rPk, rhoH;
size_t len = 33;
if (!generate_random_scalar(ctx, km) || !generate_random_scalar(ctx, kr) ||
!generate_random_scalar(ctx, krho))
return 0;
if (!secp256k1_ec_pubkey_create(ctx, &T1, kr))
return 0;
if (!secp256k1_ec_pubkey_create(ctx, &mG, km))
return 0;
rPk = *pk;
if (!secp256k1_ec_pubkey_tweak_mul(ctx, &rPk, kr))
return 0;
secp256k1_pubkey const* add_t2[2] = {&mG, &rPk};
if (!secp256k1_ec_pubkey_combine(ctx, &T2, add_t2, 2))
return 0;
get_h_generator(ctx, &H);
rhoH = H;
if (!secp256k1_ec_pubkey_tweak_mul(ctx, &rhoH, krho))
return 0;
secp256k1_pubkey const* add_t3[2] = {&mG, &rhoH};
if (!secp256k1_ec_pubkey_combine(ctx, &T3, add_t3, 2))
return 0;
unsigned char hash_input[290];
build_link_challenge_hash(
ctx, hash_input, c1, c2, pk, pcm, &T1, &T2, &T3, context_id);
SHA256(hash_input, 290, e);
for (int i = 0; i < 8; i++)
m_sc[31 - i] = (amount >> (i * 8)) & 0xFF;
memcpy(sm, m_sc, 32);
if (!secp256k1_ec_seckey_tweak_mul(ctx, sm, e))
return 0;
if (!secp256k1_ec_seckey_tweak_add(ctx, sm, km))
return 0;
memcpy(sr, r, 32);
if (!secp256k1_ec_seckey_tweak_mul(ctx, sr, e))
return 0;
if (!secp256k1_ec_seckey_tweak_add(ctx, sr, kr))
return 0;
memcpy(srho, rho, 32);
if (!secp256k1_ec_seckey_tweak_mul(ctx, srho, e))
return 0;
if (!secp256k1_ec_seckey_tweak_add(ctx, srho, krho))
return 0;
len = 33;
secp256k1_ec_pubkey_serialize(
ctx, proof, &len, &T1, SECP256K1_EC_COMPRESSED);
len = 33;
secp256k1_ec_pubkey_serialize(
ctx, proof + 33, &len, &T2, SECP256K1_EC_COMPRESSED);
len = 33;
secp256k1_ec_pubkey_serialize(
ctx, proof + 66, &len, &T3, SECP256K1_EC_COMPRESSED);
memcpy(proof + 99, sm, 32);
memcpy(proof + 131, sr, 32);
memcpy(proof + 163, srho, 32);
return 1;
}
/* --- Verifier Implementation --- */
int
secp256k1_elgamal_pedersen_link_verify(
secp256k1_context const* ctx,
unsigned char const* proof,
secp256k1_pubkey const* c1,
secp256k1_pubkey const* c2,
secp256k1_pubkey const* pk,
secp256k1_pubkey const* pcm,
unsigned char const* context_id)
{
secp256k1_pubkey T1_p, T2_p, T3_p;
secp256k1_pubkey lhs, rhs, H, mG, term2;
unsigned char sm[32], sr[32], srho[32], e[32], e_neg[32];
unsigned char hash_input[290];
if (!secp256k1_ec_pubkey_parse(ctx, &T1_p, proof, 33))
return 0;
if (!secp256k1_ec_pubkey_parse(ctx, &T2_p, proof + 33, 33))
return 0;
if (!secp256k1_ec_pubkey_parse(ctx, &T3_p, proof + 66, 33))
return 0;
memcpy(sm, proof + 99, 32);
memcpy(sr, proof + 131, 32);
memcpy(srho, proof + 163, 32);
if (secp256k1_ec_seckey_verify(ctx, sm) != 1)
return 0;
if (secp256k1_ec_seckey_verify(ctx, sr) != 1)
return 0;
if (secp256k1_ec_seckey_verify(ctx, srho) != 1)
return 0;
build_link_challenge_hash(
ctx, hash_input, c1, c2, pk, pcm, &T1_p, &T2_p, &T3_p, context_id);
SHA256(hash_input, sizeof(hash_input), e);
if (secp256k1_ec_seckey_verify(ctx, e) != 1)
return 0;
memcpy(e_neg, e, 32);
if (!secp256k1_ec_seckey_negate(ctx, e_neg))
return 0;
#define COMBINE2(out, A, B) \
do \
{ \
secp256k1_pubkey _sum; \
const secp256k1_pubkey* _pts[2] = {(A), (B)}; \
if (!secp256k1_ec_pubkey_combine(ctx, &_sum, _pts, 2)) \
return 0; \
(out) = _sum; \
} while (0)
#define EQ_PUBKEY(A, B) \
do \
{ \
unsigned char _a[33], _b[33]; \
size_t _l = 33; \
if (!secp256k1_ec_pubkey_serialize( \
ctx, _a, &_l, (A), SECP256K1_EC_COMPRESSED)) \
return 0; \
_l = 33; \
if (!secp256k1_ec_pubkey_serialize( \
ctx, _b, &_l, (B), SECP256K1_EC_COMPRESSED)) \
return 0; \
if (memcmp(_a, _b, 33) != 0) \
return 0; \
} while (0)
/* Eq 1 */
if (!secp256k1_ec_pubkey_create(ctx, &lhs, sr))
return 0;
rhs = *c1;
if (!secp256k1_ec_pubkey_tweak_mul(ctx, &rhs, e_neg))
return 0;
COMBINE2(lhs, &lhs, &rhs);
EQ_PUBKEY(&lhs, &T1_p);
/* Eq 2 */
if (!secp256k1_ec_pubkey_create(ctx, &mG, sm))
return 0;
term2 = *pk;
if (!secp256k1_ec_pubkey_tweak_mul(ctx, &term2, sr))
return 0;
COMBINE2(lhs, &mG, &term2);
rhs = *c2;
if (!secp256k1_ec_pubkey_tweak_mul(ctx, &rhs, e_neg))
return 0;
COMBINE2(lhs, &lhs, &rhs);
EQ_PUBKEY(&lhs, &T2_p);
/* Eq 3 */
get_h_generator(ctx, &H);
term2 = H;
if (!secp256k1_ec_pubkey_tweak_mul(ctx, &term2, srho))
return 0;
COMBINE2(lhs, &mG, &term2);
rhs = *pcm;
if (!secp256k1_ec_pubkey_tweak_mul(ctx, &rhs, e_neg))
return 0;
COMBINE2(lhs, &lhs, &rhs);
EQ_PUBKEY(&lhs, &T3_p);
#undef COMBINE2
#undef EQ_PUBKEY
return 1;
}
int
secp256k1_mpt_pedersen_commit(
secp256k1_context const* ctx,
secp256k1_pubkey* commitment,
uint64_t amount,
unsigned char const* rho)
{
secp256k1_pubkey mG, rH, H;
unsigned char m_scalar[32] = {0};
// 1. Calculate m * G
for (int i = 0; i < 8; i++)
{
m_scalar[31 - i] = (amount >> (i * 8)) & 0xFF;
}
if (!secp256k1_ec_pubkey_create(ctx, &mG, m_scalar))
{
return 0;
}
// 2. Calculate rho * H
get_h_generator(ctx, &H);
rH = H;
if (!secp256k1_ec_pubkey_tweak_mul(ctx, &rH, rho))
{
return 0;
}
// 3. Combine: mG + rH
secp256k1_pubkey const* points[2] = {&mG, &rH};
if (!secp256k1_ec_pubkey_combine(ctx, commitment, points, 2))
{
return 0;
}
return 1;
}
} // namespace ripple

View File

@@ -12,16 +12,6 @@
namespace ripple {
size_t
expectedProofLength(std::shared_ptr<SLE const> const& issuance)
{
auto const equalityProofLength = getEqualityProofLength(
issuance->isFieldPresent(sfAuditorElGamalPublicKey));
// todo: add pederson and range proof length
return equalityProofLength;
}
NotTEC
ConfidentialConvertBack::preflight(PreflightContext const& ctx)
{
@@ -74,9 +64,10 @@ verifyProofs(
bool const hasAuditor = issuance->isFieldPresent(sfAuditorElGamalPublicKey);
if (hasAuditor)
{
auditor.emplace(EncryptedAmountInfo{
(*issuance)[sfAuditorElGamalPublicKey],
tx[sfAuditorEncryptedAmount]});
auditor.emplace(
EncryptedAmountInfo{
(*issuance)[sfAuditorElGamalPublicKey],
tx[sfAuditorEncryptedAmount]});
}
if (auto const ter = verifyRevealedAmount(