Skip to content
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: secret service #28

Draft
wants to merge 30 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 9 additions & 26 deletions crates/secret-service-client/src/p2p.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
//! P2P signer client

use std::{future::Future, sync::Arc};
use std::sync::Arc;

use bitcoin::XOnlyPublicKey;
use musig2::secp256k1::schnorr::Signature;
use musig2::secp256k1::SecretKey;
use quinn::Connection;
use secret_service_proto::v1::{
traits::{Client, ClientError, Origin, P2PSigner},
Expand All @@ -30,28 +29,12 @@ impl P2PClient {
}

impl P2PSigner<Client> for P2PClient {
fn sign(
&self,
digest: &[u8; 32],
) -> impl Future<Output = <Client as Origin>::Container<Signature>> + Send {
async move {
let msg = ClientMessage::P2PSign { digest: *digest };
let res = make_v1_req(&self.conn, msg, self.config.timeout).await?;
let ServerMessage::P2PSign { sig } = res else {
return Err(ClientError::WrongMessage(res.into()));
};
Signature::from_slice(&sig).map_err(|_| ClientError::BadData)
}
}

fn pubkey(&self) -> impl Future<Output = <Client as Origin>::Container<XOnlyPublicKey>> + Send {
async move {
let msg = ClientMessage::P2PPubkey;
let res = make_v1_req(&self.conn, msg, self.config.timeout).await?;
let ServerMessage::P2PPubkey { pubkey } = res else {
return Err(ClientError::WrongMessage(res.into()));
};
XOnlyPublicKey::from_slice(&pubkey).map_err(|_| ClientError::BadData)
}
async fn secret_key(&self) -> <Client as Origin>::Container<SecretKey> {
let msg = ClientMessage::P2PSecretKey;
let res = make_v1_req(&self.conn, msg, self.config.timeout).await?;
let ServerMessage::P2PSecretKey { key } = res else {
return Err(ClientError::WrongMessage(res.into()));
};
Ok(SecretKey::from_slice(&key).expect("correct length"))
}
}
9 changes: 3 additions & 6 deletions crates/secret-service-proto/src/v1/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::future::Future;
use bitcoin::{Txid, XOnlyPublicKey};
use musig2::{
errors::{RoundContributionError, RoundFinalizeError},
secp256k1::schnorr::Signature,
secp256k1::{schnorr::Signature, SecretKey},
AggNonce, LiftedSignature, PartialSignature, PubNonce,
};
use quinn::{ConnectionError, ReadExactError, WriteError};
Expand Down Expand Up @@ -77,11 +77,8 @@ pub trait OperatorSigner<O: Origin>: Send {
/// The user should make sure the operator's secret key should have its own unique key that isn't
/// used for any other purpose.
pub trait P2PSigner<O: Origin>: Send {
/// Signs a `digest` using the operator's [`SecretKey`](bitcoin::secp256k1::SecretKey).
fn sign(&self, digest: &[u8; 32]) -> impl Future<Output = O::Container<Signature>> + Send;

/// Returns the public key of the operator's secret key.
fn pubkey(&self) -> impl Future<Output = O::Container<XOnlyPublicKey>> + Send;
/// Returns the [`SecretKey`] that should be used for signing P2P messages
fn secret_key(&self) -> impl Future<Output = O::Container<SecretKey>> + Send;
}

/// Uniquely identifies an in-memory MuSig2 session on the signing server.
Expand Down
24 changes: 6 additions & 18 deletions crates/secret-service-proto/src/v1/wire.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,10 @@ pub enum ServerMessage {
pubkey: [u8; 32],
},

/// Response for [`P2PSigner::sign`](super::traits::P2PSigner::sign).
P2PSign {
/// Schnorr signature of for a certain message.
sig: [u8; 64],
},

/// Response for [`P2PSigner::pubkey`](super::traits::P2PSigner::pubkey).
P2PPubkey {
/// Serialized Schnorr [`XOnlyPublicKey`] for P2P signatures.
pubkey: [u8; 32],
/// Response for [`P2PSigner::secret_key`](super::traits::P2PSigner::secret_key).
P2PSecretKey {
/// Serialized [`SecretKey`](musig::secp256k1::SecretKey)
key: [u8; 32],
},

/// Response for [`Musig2Signer::new_session`](super::traits::Musig2Signer::new_session).
Expand Down Expand Up @@ -197,14 +191,8 @@ pub enum ClientMessage {
/// Request for [`OperatorSigner::pubkey`](super::traits::OperatorSigner::pubkey).
OperatorPubkey,

/// Request for [`P2PSigner::sign`](super::traits::P2PSigner::sign).
P2PSign {
/// The digest of the data the client wants signed.
digest: [u8; 32],
},

/// Request for [`P2PSigner::pubkey`](super::traits::P2PSigner::pubkey).
P2PPubkey,
/// Request for [`P2PSigner::secret_key`](super::traits::P2PSigner::secret_key).
P2PSecretKey,

/// Request for [`Musig2Signer::new_session`](super::traits::Musig2Signer::new_session).
Musig2NewSession {
Expand Down
15 changes: 4 additions & 11 deletions crates/secret-service-server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,17 +205,10 @@ where
}
}

ArchivedClientMessage::P2PSign { digest } => {
let sig = service.p2p_signer().sign(digest).await;
ServerMessage::P2PSign {
sig: sig.serialize(),
}
}

ArchivedClientMessage::P2PPubkey => {
let pubkey = service.p2p_signer().pubkey().await;
ServerMessage::P2PPubkey {
pubkey: pubkey.serialize(),
ArchivedClientMessage::P2PSecretKey => {
let key = service.p2p_signer().secret_key().await;
ServerMessage::P2PSecretKey {
key: key.secret_bytes(),
}
}

Expand Down
22 changes: 7 additions & 15 deletions crates/secret-service/src/disk/p2p.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,24 @@
//! In-memory persistence for operator's P2P secret data.

use std::future::Future;

use bitcoin::{key::Keypair, XOnlyPublicKey};
use musig2::secp256k1::{schnorr::Signature, Message, SecretKey, SECP256K1};
use secret_service_proto::v1::traits::{P2PSigner, Server};
use musig2::secp256k1::SecretKey;
use secret_service_proto::v1::traits::{Origin, P2PSigner, Server};

/// Secret data for the P2P signer.
#[derive(Debug)]
pub struct ServerP2PSigner {
/// The [`Keypair`] for the P2P signer.
kp: Keypair,
/// The [`SecretKey`] for the P2P signer.
sk: SecretKey,
}

impl ServerP2PSigner {
/// Creates a new [`ServerP2PSigner`] with the given secret key.
pub fn new(sk: SecretKey) -> Self {
let kp = Keypair::from_secret_key(SECP256K1, &sk);
Self { kp }
Self { sk }
}
}

impl P2PSigner<Server> for ServerP2PSigner {
fn sign(&self, digest: &[u8; 32]) -> impl Future<Output = Signature> + Send {
async move { self.kp.sign_schnorr(Message::from_digest(*digest)) }
}

fn pubkey(&self) -> impl Future<Output = XOnlyPublicKey> + Send {
async move { self.kp.x_only_public_key().0 }
async fn secret_key(&self) -> <Server as Origin>::Container<SecretKey> {
self.sk
}
}
1 change: 0 additions & 1 deletion crates/secret-service/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
//! Runs the Secret Service.
#![allow(clippy::manual_async_fn)]

// use secret_service_server::rustls::ServerConfig;
pub mod config;
Expand Down
7 changes: 1 addition & 6 deletions crates/secret-service/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,7 @@ async fn e2e() {

// p2p signer
let p2p_signer = client.p2p_signer();
let pubkey = p2p_signer.pubkey().await.expect("good response");
let to_sign = rng.gen();
let sig = p2p_signer.sign(&to_sign).await.expect("good response");
assert!(secp_ctx
.verify_schnorr(&sig, &Message::from_digest(to_sign), &pubkey)
.is_ok());
p2p_signer.secret_key().await.expect("good response");
}

/// Dummy certificate verifier that treats any certificate as valid.
Expand Down
Loading