crypto/ecdh.cpp¶
Namespaces¶
| Name |
|---|
| rlpx |
| rlpx::crypto |
Source code¶
// Copyright 2025 GeniusVentures
// SPDX-License-Identifier: Apache-2.0
#include <rlpx/crypto/ecdh.hpp>
#include <base/rlp-logger.hpp>
#include <secp256k1.h>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include <cstring>
namespace rlpx::crypto {
namespace {
// Global secp256k1 context (thread-safe, read-only after creation)
secp256k1_context* get_context() {
static secp256k1_context* ctx = secp256k1_context_create(
SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY
);
return ctx;
}
rlp::base::Logger& ecdh_log() {
static auto log = rlp::base::createLogger("rlpx.ecdh");
return log;
}
}
CryptoResult<SharedSecret> Ecdh::compute_shared_secret(
gsl::span<const uint8_t, kPublicKeySize> public_key,
gsl::span<const uint8_t, kPrivateKeySize> private_key
) noexcept {
auto* ctx = get_context();
if ( !ctx ) {
return CryptoError::kSecp256k1Error;
}
// Verify private key is valid
if ( !secp256k1_ec_seckey_verify(ctx, private_key.data()) ) {
ecdh_log()->debug("compute_shared_secret: secp256k1_ec_seckey_verify failed");
return CryptoError::kInvalidPrivateKey;
}
// Parse public key (uncompressed format: 0x04 prefix + 64 bytes = kUncompressedPubKeySize)
secp256k1_pubkey parsed_pubkey;
std::array<uint8_t, kUncompressedPubKeySize> pubkey_with_prefix;
pubkey_with_prefix[0] = kUncompressedPubKeyPrefix;
std::memcpy(pubkey_with_prefix.data() + kUncompressedPubKeyPrefixSize, public_key.data(), kPublicKeySize);
if ( !secp256k1_ec_pubkey_parse(ctx, &parsed_pubkey, pubkey_with_prefix.data(), kUncompressedPubKeySize) ) {
ecdh_log()->debug("compute_shared_secret: secp256k1_ec_pubkey_parse failed");
return CryptoError::kInvalidPublicKey;
}
// Compute ECDH shared point
SharedSecret shared_secret;
// Compute pubkey * privkey using secp256k1_ec_pubkey_tweak_mul equivalent
secp256k1_pubkey result_pubkey = parsed_pubkey;
if ( !secp256k1_ec_pubkey_tweak_mul(ctx, &result_pubkey, private_key.data()) ) {
ecdh_log()->debug("compute_shared_secret: secp256k1_ec_pubkey_tweak_mul failed");
return CryptoError::kEcdhFailed;
}
// Serialize the result (uncompressed format: 0x04 || x || y = kUncompressedPubKeySize bytes)
unsigned char serialized[kUncompressedPubKeySize];
size_t output_len = kUncompressedPubKeySize;
if ( !secp256k1_ec_pubkey_serialize(ctx, serialized, &output_len, &result_pubkey, SECP256K1_EC_UNCOMPRESSED) ) {
ecdh_log()->debug("compute_shared_secret: secp256k1_ec_pubkey_serialize failed");
return CryptoError::kEcdhFailed;
}
// Extract x-coordinate (bytes after 0x04 prefix, skipping kUncompressedPubKeyPrefixSize byte)
std::memcpy(shared_secret.data(), serialized + kUncompressedPubKeyPrefixSize, kSharedSecretSize);
return shared_secret;
}
CryptoResult<Ecdh::KeyPair> Ecdh::generate_ephemeral_keypair() noexcept {
auto* ctx = get_context();
if ( !ctx ) {
return CryptoError::kSecp256k1Error;
}
KeyPair keypair;
// Generate random private key
while ( true ) {
if ( RAND_bytes(keypair.private_key.data(), kPrivateKeySize) != 1 ) {
return CryptoError::kOpenSslError;
}
// Verify it's a valid private key
if ( secp256k1_ec_seckey_verify(ctx, keypair.private_key.data()) ) {
break;
}
// If invalid, try again (very rare)
}
// Compute corresponding public key
secp256k1_pubkey pubkey;
if ( !secp256k1_ec_pubkey_create(ctx, &pubkey, keypair.private_key.data()) ) {
return CryptoError::kSecp256k1Error;
}
// Serialize public key in uncompressed format (kUncompressedPubKeySize bytes)
std::array<uint8_t, kUncompressedPubKeySize> serialized;
size_t output_len = kUncompressedPubKeySize;
if ( !secp256k1_ec_pubkey_serialize(
ctx,
serialized.data(),
&output_len,
&pubkey,
SECP256K1_EC_UNCOMPRESSED
) ) {
return CryptoError::kSecp256k1Error;
}
// Copy public key (skip 0x04 prefix byte)
std::memcpy(keypair.public_key.data(), serialized.data() + kUncompressedPubKeyPrefixSize, kPublicKeySize);
return keypair;
}
bool Ecdh::verify_public_key(gsl::span<const uint8_t, kPublicKeySize> public_key) noexcept {
auto* ctx = get_context();
if ( !ctx ) {
return false;
}
// Add 0x04 prefix for uncompressed format
std::array<uint8_t, kUncompressedPubKeySize> pubkey_with_prefix;
pubkey_with_prefix[0] = kUncompressedPubKeyPrefix;
std::memcpy(pubkey_with_prefix.data() + kUncompressedPubKeyPrefixSize, public_key.data(), kPublicKeySize);
secp256k1_pubkey parsed_pubkey;
return secp256k1_ec_pubkey_parse(ctx, &parsed_pubkey, pubkey_with_prefix.data(), kUncompressedPubKeySize) == 1;
}
} // namespace rlpx::crypto
Updated on 2026-04-13 at 23:22:46 -0700