diff --git a/include/xrpl/protocol/ConfidentialTransfer.h b/include/xrpl/protocol/ConfidentialTransfer.h index 776b66e542..904ae00436 100644 --- a/include/xrpl/protocol/ConfidentialTransfer.h +++ b/include/xrpl/protocol/ConfidentialTransfer.h @@ -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 diff --git a/src/libxrpl/protocol/ConfidentialTransfer.cpp b/src/libxrpl/protocol/ConfidentialTransfer.cpp index 2711061914..b4b91b2829 100644 --- a/src/libxrpl/protocol/ConfidentialTransfer.cpp +++ b/src/libxrpl/protocol/ConfidentialTransfer.cpp @@ -22,9 +22,9 @@ #include #include +#include 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)) diff --git a/src/test/app/ConfidentialTransfer_test.cpp b/src/test/app/ConfidentialTransfer_test.cpp index 1c89fc82c5..20da2a7f28 100644 --- a/src/test/app/ConfidentialTransfer_test.cpp +++ b/src/test/app/ConfidentialTransfer_test.cpp @@ -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(); diff --git a/src/xrpld/app/tx/detail/ConfidentialConvert.cpp b/src/xrpld/app/tx/detail/ConfidentialConvert.cpp index ba10568535..5e12e5f1a4 100644 --- a/src/xrpld/app/tx/detail/ConfidentialConvert.cpp +++ b/src/xrpld/app/tx/detail/ConfidentialConvert.cpp @@ -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 {