I am trying to implement the Ethereum handshake procedure (RLPx) at low level, as specified here:
https://github.com/ethereum/devp2p/blob/master/rlpx.md
I am using the k256 crate which provides facilities for working with ECDH and ECDSA.
I’d like to combine the two, but I haven’t been able to.
The crate’s ECDSA signing function, sign()
, does not seem to support its ecdh::EphemeralSecret
, which is required for the Diffie-Hellman key exchange procedure, so I need to work with it. It only seems to support ecdsa::SigningKey
, but that is not supported by their ECDH, which only supports ecdh::EphemeralSecret
.
ECDH documentation
ECDSA documentation
I have something like:
use ethereum_types::H256;
use hex;
use k256::{ecdh::EphemeralSecret, EncodedPoint, PublicKey, SecretKey};
use k256::ecdsa::{self, Signature, signature::Signer, SigningKey};
use k256::ecdsa::signature::SignerMut;
use rand_core::OsRng;
use rlp::RlpStream;
use secrecy::{ExposeSecret, Secret, Zeroize};
...
let initiator_public_key = static_secret_key.public_key();
let initiator_public_key = &(*(initiator_public_key.to_sec1_bytes()))[1..];
let username = hex::decode(username)?;
let mut aux = [0; 65];
aux[0] = 4;
aux[1..].copy_from_slice(username.as_ref());
let recipient_public_key = PublicKey::from_sec1_bytes(aux.as_ref())?;
let init_ephemeral_secret = EphemeralSecret::random(&mut OsRng);
let init_ephemeral_secret = Secret::new(init_ephemeral_secret);
let init_shared = init_ephemeral_secret
.expose_secret()
.diffie_hellman(&recipient_public_key);
let init_shared = H256::from_slice(&init_shared.raw_secret_bytes()[..32]);
let initiator_nonce = H256::random();
// compute signature
let message = init_shared ^ initiator_nonce;
let signature: Signature = init_ephemeral_secret
.expose_secret()
.sign(message.as_fixed_bytes()); // ERROR
// "auth_body" is an RLP stream of 4 values
let mut rlp_stream = RlpStream::new_list(4);
rlp_stream.append(&signature);
rlp_stream.append(&initiator_public_key);
rlp_stream.append(&initiator_nonce);
rlp_stream.append(&5);
let auth_body = rlp_stream.out();
...
username
is the recipient’s public key from its enode – the first part, before the IP address.
The error that I am getting is:
error[E0599]: the method `sign` exists for reference `&EphemeralSecret<Secp256k1>`, but its trait bounds were not satisfied
113 | let signature: Signature = init_ephemeral_secret
| ________________________________-
114 | | .expose_secret()
115 | | .sign(message.as_fixed_bytes());
| | -^^^^ method cannot be called on `&EphemeralSecret<Secp256k1>` due to unsatisfied trait bounds
| |_________|
93 | pub struct EphemeralSecret<C>
| ----------------------------- doesn't satisfy `_: Signer<_>` or `_: SignerMut<_>`
|
= note: the following trait bounds were not satisfied:
`&k256::elliptic_curve::ecdh::EphemeralSecret<k256::Secp256k1>: k256::ecdsa::signature::Signer<_>`
which is required by `&k256::elliptic_curve::ecdh::EphemeralSecret<k256::Secp256k1>: k256::ecdsa::signature::SignerMut<_>`
`k256::elliptic_curve::ecdh::EphemeralSecret<k256::Secp256k1>: k256::ecdsa::signature::Signer<_>`
which is required by `k256::elliptic_curve::ecdh::EphemeralSecret<k256::Secp256k1>: k256::ecdsa::signature::SignerMut<_>`
How can I make it work?
2