-
Notifications
You must be signed in to change notification settings - Fork 24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: support custom KeyProvider #366
Conversation
286ac25
to
6a85133
Compare
Could you provide an example how the library user can add a custom signer? |
Implement these two traits, secio just provides the default implementation of secp256k1 |
6a85133
to
0791074
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there are several things to improve in this PR
-
The naming needs to be unified:
Config
struct field named askey
andServiceBuilder
struct field named askey_pair
, however, the trait is named asSigner
, it's really confusing for reviewer. Can we unify the field naming assigner
and the trait naming asSigner
? -
We should not restrict the user to implement a
Debug
trait for Signer, suggest to remove this trait bound fromSigner
andPubkey
. -
The message type of T should not be restricted to
Send
if I understand correctly, suggest to remove this trait bound inSigner
andPubKey
trait:
- fn sign_ecdsa<T: AsRef<[u8]> + Send>(&self, message: T) -> Result<Vec<u8>, Self::Error>;
+ fn sign_ecdsa<T: AsRef<[u8]>>(&self, message: T) -> Result<Vec<u8>, Self::Error>;
-
It is not necessary to use
Arc<K>
as aSigner
field by default, the only usage ofArc
is for cloning, we may change it tokey_pair: Option<K>
by adding aClone
trait bound toSigner: Clone +
. User may choose to pass aArc<K>
orK
toServiceBuilder
-
In the
handshake
procedure, why we need to use a local signer to recover the remote public key, shouldn't these two be unrelated?
let remote_public_key =
<K as Signer>::pubkey_from_slice(ephemeral_context.state.remote.public_key.inner_ref())
.map_err(Into::into)?;
if !remote_public_key.verify_ecdsa(&data_to_verify, &remote_exchanges.signature) {
debug!("failed to verify the remote's signature");
return Err(SecioError::SignatureVerificationFailed);
}
The reason for using signer to restore the public key here is to let users understand that verification and signature need to use the same algorithm. Of course, this function of restoring the public key can also be placed on the pubkey trait. This is what I have thought about for a long time. It's will like follow: pub trait Signer: std::fmt::Debug + Send + Sync + 'static {
/// Error
type Error: Into<crate::error::SecioError>;
/// Public key
type Pubkey: Pubkey;
/// Constructs a signature for `msg` using the secret key `sk`
#[cfg(feature = "async-trait")]
async fn sign_ecdsa_async<T: AsRef<[u8]> + Send>(
&self,
message: T,
) -> Result<Vec<u8>, Self::Error> {
self.sign_ecdsa(message)
}
/// Constructs a signature for `msg` using the secret key `sk`
fn sign_ecdsa<T: AsRef<[u8]>>(&self, message: T) -> Result<Vec<u8>, Self::Error>;
/// Creates a new public key from a [`Signer`].
fn pubkey(&self) -> Self::Pubkey;
}
/// Public key for Signer
pub trait Pubkey: Send + Sync + 'static {
/// Error
type Error: Into<crate::error::SecioError>;
/// Checks that `sig` is a valid ECDSA signature for `msg` using the public
/// key `pubkey`.
fn verify_ecdsa<T: AsRef<[u8]>, F: AsRef<[u8]>>(&self, message: T, signature: F) -> bool;
/// serialized key into a bytes
fn serialize(&self) -> Vec<u8>;
/// Recover public key from slice
fn from_slice<T: AsRef<[u8]>>(key: T) -> Result<Self, Self::Error>
where
Self: Sized;
}
let remote_public_key =
<K as Signer>::Pubkey::from_slice(ephemeral_context.state.remote.public_key.inner_ref())
.map_err(Into::into)?; |
67ce726
to
6586f3a
Compare
07c00cd
to
6876a2b
Compare
in this case, we may simplified trait to pub trait Signer {
fn sign_ecdsa<T: AsRef<[u8]>>(&self, message: T) -> Result<Vec<u8>, Self::Error>;
fn pubkey(&self) -> Vec<u8>;
fn verify_ecdsa<P: AsRef<[u8]>, T: AsRef<[u8]>, F: AsRef<[u8]>>(pubkey: P, message: T, signature: F) -> bool;
} |
@@ -157,7 +176,7 @@ impl ServiceBuilder { | |||
/// | |||
/// for example, set all tcp bind to `127.0.0.1:1080`, set keepalive: | |||
/// | |||
/// ```rust | |||
/// ```ignore |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why ignore doc test here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since ServiceBuilder
added a new generic type
let mut server = ServiceBuilder::new();
must specify the type,I think it's noise, so I ignore this doc test
This is a feature with a wide influence and break change. I have tried several methods, and finally feel that the current writing method is relatively appropriate.
It will allow users to decide how to keep the private key used for ecdh, such as using some private key management services, requiring network communication signatures, and of course a default implementation is also provided. Users don't even need to use the secp256k1 algorithm, such as ed25519, as long as they conform to the behavior specification of the trait.
Of course, it also contains some minor modifications, including api changes, etc.