Update mpt-crypto with zero encryption (#5905)

This commit is contained in:
Shawn Xie
2025-10-17 11:41:39 -04:00
committed by GitHub
parent 8fdc639206
commit bbc3071fd1
4 changed files with 133 additions and 39 deletions

View File

@@ -35,12 +35,18 @@
namespace ripple {
/**
* @brief Generates a new secp256k1 key pair.
*/
SECP256K1_API int
secp256k1_elgamal_generate_keypair(
secp256k1_context const* ctx,
unsigned char* privkey,
secp256k1_pubkey* pubkey);
/**
* @brief Encrypts a 64-bit amount using ElGamal.
*/
SECP256K1_API int
secp256k1_elgamal_encrypt(
secp256k1_context const* ctx,
@@ -50,6 +56,9 @@ secp256k1_elgamal_encrypt(
uint64_t amount,
unsigned char const* blinding_factor);
/**
* @brief Decrypts an ElGamal ciphertext to recover the amount.
*/
SECP256K1_API int
secp256k1_elgamal_decrypt(
secp256k1_context const* ctx,
@@ -58,6 +67,9 @@ secp256k1_elgamal_decrypt(
secp256k1_pubkey const* c2,
unsigned char const* privkey);
/**
* @brief Homomorphically adds two ElGamal ciphertexts.
*/
SECP256K1_API int
secp256k1_elgamal_add(
secp256k1_context const* ctx,
@@ -68,6 +80,9 @@ secp256k1_elgamal_add(
secp256k1_pubkey const* b_c1,
secp256k1_pubkey const* b_c2);
/**
* @brief Homomorphically subtracts two ElGamal ciphertexts.
*/
SECP256K1_API int
secp256k1_elgamal_subtract(
secp256k1_context const* ctx,
@@ -78,6 +93,19 @@ secp256k1_elgamal_subtract(
secp256k1_pubkey const* b_c1,
secp256k1_pubkey const* b_c2);
/**
* @brief Generates the canonical encrypted zero for a given trust line.
*/
SECP256K1_API int
generate_canonical_encrypted_zero(
secp256k1_context const* ctx,
secp256k1_pubkey* enc_zero_c1,
secp256k1_pubkey* enc_zero_c2,
secp256k1_pubkey const* pubkey,
char const* acct,
char const* issuer,
char const* curr);
// breaks a 66-byte encrypted amount into two 33-byte components
// then parses each 33-byte component into 64-byte secp256k1_pubkey format
bool

View File

@@ -22,9 +22,9 @@
#include <xrpl/protocol/TER.h>
#include <openssl/rand.h>
#include <openssl/sha.h>
namespace ripple {
int
secp256k1_elgamal_generate_keypair(
secp256k1_context const* ctx,
@@ -61,33 +61,59 @@ secp256k1_elgamal_encrypt(
uint64_t amount,
unsigned char const* blinding_factor)
{
unsigned char amount_scalar[32] = {0};
secp256k1_pubkey M, S;
secp256k1_pubkey const* points_to_add[2];
secp256k1_pubkey S;
// CORRECTED: Convert uint64_t to a 32-byte BIG-ENDIAN scalar.
for (int i = 0; i < 8; ++i)
// First, calculate C1 = k * G
if (secp256k1_ec_pubkey_create(ctx, c1, blinding_factor) != 1)
{
amount_scalar[31 - i] = (amount >> (i * 8)) & 0xFF;
return 0;
}
if (secp256k1_ec_pubkey_create(ctx, &M, amount_scalar) != 1)
return 0;
if (secp256k1_ec_pubkey_create(ctx, c1, blinding_factor) != 1)
return 0;
// Next, calculate the shared secret S = k * Q
S = *pubkey_Q;
if (secp256k1_ec_pubkey_tweak_mul(ctx, &S, blinding_factor) != 1)
{
return 0;
}
points_to_add[0] = &M;
points_to_add[1] = &S;
if (secp256k1_ec_pubkey_combine(ctx, c2, points_to_add, 2) != 1)
return 0;
// --- Handle the amount ---
if (amount == 0)
{
// For amount = 0, C2 = S.
*c2 = S;
}
else
{
// For non-zero amounts, proceed as before.
unsigned char amount_scalar[32] = {0};
secp256k1_pubkey M;
secp256k1_pubkey const* points_to_add[2];
return 1;
// Convert amount to a 32-byte BIG-ENDIAN scalar.
for (int i = 0; i < 8; ++i)
{
amount_scalar[31 - i] = (amount >> (i * 8)) & 0xFF;
}
// Calculate M = amount * G
if (secp256k1_ec_pubkey_create(ctx, &M, amount_scalar) != 1)
{
return 0;
}
// Calculate C2 = M + S
points_to_add[0] = &M;
points_to_add[1] = &S;
if (secp256k1_ec_pubkey_combine(ctx, c2, points_to_add, 2) != 1)
{
return 0;
}
}
return 1; // Success
}
// ... implementation of secp256k1_elgamal_encrypt ...
// ... implementation of secp256k1_elgamal_decrypt ...
int
secp256k1_elgamal_decrypt(
secp256k1_context const* ctx,
@@ -96,21 +122,27 @@ secp256k1_elgamal_decrypt(
secp256k1_pubkey const* c2,
unsigned char const* privkey)
{
/* C90-compliant variable declarations */
secp256k1_pubkey S, M, G_point, current_M, next_M;
secp256k1_pubkey const* points_to_add[2];
unsigned char c2_bytes[33], s_bytes[33], m_bytes[33], current_m_bytes[33];
size_t len;
uint64_t i;
// CORRECTED: Create the scalar '1' in big-endian format.
/* Create the scalar '1' in big-endian format */
unsigned char one_scalar[32] = {0};
one_scalar[31] = 1;
/* --- Executable Code --- */
// 1. Calculate S = privkey * C1
S = *c1;
if (secp256k1_ec_pubkey_tweak_mul(ctx, &S, privkey) != 1)
{
return 0;
}
// CORRECTED: Reset 'len' before each serialize call.
// 2. Check for amount = 0 by comparing serialized points
len = sizeof(c2_bytes);
if (secp256k1_ec_pubkey_serialize(
ctx, c2_bytes, &len, c2, SECP256K1_EC_COMPRESSED) != 1)
@@ -125,23 +157,28 @@ secp256k1_elgamal_decrypt(
return 1;
}
// 3. Recover M = C2 - S
if (secp256k1_ec_pubkey_negate(ctx, &S) != 1)
return 0;
points_to_add[0] = c2;
points_to_add[1] = &S;
if (secp256k1_ec_pubkey_combine(ctx, &M, points_to_add, 2) != 1)
{
return 0;
}
// 4. Serialize M once for comparison in the loop
len = sizeof(m_bytes);
if (secp256k1_ec_pubkey_serialize(
ctx, m_bytes, &len, &M, SECP256K1_EC_COMPRESSED) != 1)
return 0;
// 5. Brute-force search loop
if (secp256k1_ec_pubkey_create(ctx, &G_point, one_scalar) != 1)
return 0;
current_M = G_point;
for (i = 1; i <= 100000; ++i)
for (i = 1; i <= 1000000; ++i)
{
len = sizeof(current_m_bytes);
if (secp256k1_ec_pubkey_serialize(
@@ -164,7 +201,7 @@ secp256k1_elgamal_decrypt(
current_M = next_M;
}
return 0;
return 0; // Not found
}
int
@@ -229,6 +266,41 @@ secp256k1_elgamal_subtract(
return 1; // Success
}
// The canonical encrypted zero
int
generate_canonical_encrypted_zero(
secp256k1_context const* ctx,
secp256k1_pubkey* enc_zero_c1,
secp256k1_pubkey* enc_zero_c2,
secp256k1_pubkey const* pubkey,
char const* acct,
char const* issuer,
char const* curr)
{
unsigned char deterministic_scalar[32];
char input_str[256];
/* 1. Create the input string for hashing */
snprintf(input_str, sizeof(input_str), "EncZero%s%s%s", acct, issuer, curr);
/* 2. Hash the string to create the deterministic scalar 'r' */
do
{
SHA256(
(unsigned char*)input_str, strlen(input_str), deterministic_scalar);
} while (secp256k1_ec_seckey_verify(ctx, deterministic_scalar) != 1);
/* 3. Encrypt the amount 0 using the deterministic scalar */
return secp256k1_elgamal_encrypt(
ctx,
enc_zero_c1,
enc_zero_c2,
pubkey,
0, /* The amount is zero */
deterministic_scalar);
}
bool
makeEcPair(Slice const& buffer, secp256k1_pubkey& out1, secp256k1_pubkey& out2)
{
@@ -358,8 +430,7 @@ encryptAmount(
secp256k1_pubkey pubKey;
std::memcpy(pubKey.data, pubKeySlice.data(), ecPubKeyLength);
std::cout << "\n encryptAmount pub key " << strHex(pubKeySlice)
<< std::endl;
// Encrypt the amount
if (!secp256k1_elgamal_encrypt(
secp256k1Context(), &c1, &c2, &pubKey, amt, blinding_factor))

View File

@@ -55,16 +55,11 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
mptAlice.generateKeyPair(bob);
auto const issuerAmt = mptAlice.encryptAmount(alice, 10);
auto const holderAmt = mptAlice.encryptAmount(bob, 10);
mptAlice.convert({
.account = bob,
.amt = 10,
.amt = 0,
.proof = "123",
.holderPubKey = mptAlice.getPubKey(bob),
.holderEncryptedAmt = holderAmt,
.issuerEncryptedAmt = issuerAmt,
});
env.close();

View File

@@ -178,16 +178,16 @@ ConfidentialConvert::doApply()
(*sleMptoken)[sfIssuerEncryptedBalance] = issuerEc;
(*sleMptoken)[sfConfidentialBalanceVersion] = 0;
// // encrypt sfConfidentialBalanceSpending with zero balance
// Buffer out(ecGamalEncryptedTotalLength);
// if (TER res = encryptAmount(
// account_, 1, (*sleMptoken)[sfHolderElGamalPublicKey], out);
// !isTesSuccess(res))
// {
// return tecINTERNAL;
// }
// encrypt sfConfidentialBalanceSpending with zero balance
Buffer out(ecGamalEncryptedTotalLength);
if (TER res = encryptAmount(
account_, 0, (*sleMptoken)[sfHolderElGamalPublicKey], out);
!isTesSuccess(res))
{
return tecINTERNAL;
}
// (*sleMptoken)[sfConfidentialBalanceSpending] = out;
(*sleMptoken)[sfConfidentialBalanceSpending] = out;
}
else
{