client.rs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. use std::path::Path;
  2. use cdk_common::error::Error;
  3. use cdk_common::grpc::VERSION_HEADER;
  4. use cdk_common::{BlindSignature, BlindedMessage, Proof};
  5. use tonic::metadata::MetadataValue;
  6. use tonic::transport::{Certificate, Channel, ClientTlsConfig, Identity};
  7. use crate::proto::signatory_client::SignatoryClient;
  8. use crate::signatory::{RotateKeyArguments, Signatory, SignatoryKeySet, SignatoryKeysets};
  9. /// A client for the Signatory service.
  10. #[allow(missing_debug_implementations)]
  11. pub struct SignatoryRpcClient {
  12. client: SignatoryClient<tonic::transport::Channel>,
  13. url: String,
  14. }
  15. #[derive(thiserror::Error, Debug)]
  16. /// Client Signatory Error
  17. pub enum ClientError {
  18. /// Transport error
  19. #[error(transparent)]
  20. Transport(#[from] tonic::transport::Error),
  21. /// IO-related errors
  22. #[error(transparent)]
  23. Io(#[from] std::io::Error),
  24. /// Signatory Error
  25. #[error(transparent)]
  26. Signatory(#[from] cdk_common::error::Error),
  27. /// Invalid URL
  28. #[error("Invalid URL")]
  29. InvalidUrl,
  30. }
  31. /// Helper function to add version header to a request
  32. fn with_version_header<T>(mut request: tonic::Request<T>) -> tonic::Request<T> {
  33. request.metadata_mut().insert(
  34. VERSION_HEADER,
  35. MetadataValue::from_static(cdk_common::SIGNATORY_PROTOCOL_VERSION),
  36. );
  37. request
  38. }
  39. impl SignatoryRpcClient {
  40. /// Create a new RemoteSigner from a tonic transport channel.
  41. pub async fn new<A: AsRef<Path>>(url: String, tls_dir: Option<A>) -> Result<Self, ClientError> {
  42. #[cfg(not(target_arch = "wasm32"))]
  43. if rustls::crypto::CryptoProvider::get_default().is_none() {
  44. let _ = rustls::crypto::ring::default_provider().install_default();
  45. }
  46. let channel = if let Some(tls_dir) = tls_dir {
  47. let tls_dir = tls_dir.as_ref();
  48. let server_root_ca_cert = std::fs::read_to_string(tls_dir.join("ca.pem"))?;
  49. let server_root_ca_cert = Certificate::from_pem(server_root_ca_cert);
  50. let client_cert = std::fs::read_to_string(tls_dir.join("client.pem"))?;
  51. let client_key = std::fs::read_to_string(tls_dir.join("client.key"))?;
  52. let client_identity = Identity::from_pem(client_cert, client_key);
  53. let tls = ClientTlsConfig::new()
  54. .ca_certificate(server_root_ca_cert)
  55. .identity(client_identity);
  56. Channel::from_shared(url.clone())
  57. .map_err(|_| ClientError::InvalidUrl)?
  58. .tls_config(tls)?
  59. .connect()
  60. .await?
  61. } else {
  62. Channel::from_shared(url.clone())
  63. .map_err(|_| ClientError::InvalidUrl)?
  64. .connect()
  65. .await?
  66. };
  67. Ok(Self {
  68. client: SignatoryClient::new(channel),
  69. url,
  70. })
  71. }
  72. }
  73. macro_rules! handle_error {
  74. ($x:expr, $y:ident, scalar) => {{
  75. let mut obj = $x.into_inner();
  76. if let Some(err) = obj.error.take() {
  77. return Err(err.into());
  78. }
  79. obj.$y
  80. }};
  81. ($x:expr, $y:ident) => {{
  82. let mut obj = $x.into_inner();
  83. if let Some(err) = obj.error.take() {
  84. return Err(err.into());
  85. }
  86. obj.$y
  87. .take()
  88. .ok_or(Error::Custom("Internal error".to_owned()))?
  89. }};
  90. }
  91. #[async_trait::async_trait]
  92. impl Signatory for SignatoryRpcClient {
  93. fn name(&self) -> String {
  94. format!("Rpc Signatory {}", self.url)
  95. }
  96. #[tracing::instrument(skip_all)]
  97. async fn blind_sign(&self, request: Vec<BlindedMessage>) -> Result<Vec<BlindSignature>, Error> {
  98. let req = super::BlindedMessages {
  99. blinded_messages: request
  100. .into_iter()
  101. .map(|blind_message| blind_message.into())
  102. .collect(),
  103. operation: super::Operation::Unspecified.into(),
  104. correlation_id: "".to_owned(),
  105. };
  106. self.client
  107. .clone()
  108. .blind_sign(with_version_header(tonic::Request::new(req)))
  109. .await
  110. .map(|response| {
  111. handle_error!(response, sigs)
  112. .blind_signatures
  113. .into_iter()
  114. .map(|blinded_signature| blinded_signature.try_into())
  115. .collect()
  116. })
  117. .map_err(|e| Error::Custom(e.to_string()))?
  118. }
  119. #[tracing::instrument(skip_all)]
  120. async fn verify_proofs(&self, proofs: Vec<Proof>) -> Result<(), Error> {
  121. let req: super::Proofs = proofs.into();
  122. self.client
  123. .clone()
  124. .verify_proofs(with_version_header(tonic::Request::new(req)))
  125. .await
  126. .map(|response| {
  127. if handle_error!(response, success, scalar) {
  128. Ok(())
  129. } else {
  130. Err(Error::SignatureMissingOrInvalid)
  131. }
  132. })
  133. .map_err(|e| Error::Custom(e.to_string()))?
  134. }
  135. #[tracing::instrument(skip_all)]
  136. async fn keysets(&self) -> Result<SignatoryKeysets, Error> {
  137. self.client
  138. .clone()
  139. .keysets(with_version_header(tonic::Request::new(
  140. super::EmptyRequest {},
  141. )))
  142. .await
  143. .map(|response| handle_error!(response, keysets).try_into())
  144. .map_err(|e| Error::Custom(e.to_string()))?
  145. }
  146. #[tracing::instrument(skip(self))]
  147. async fn rotate_keyset(&self, args: RotateKeyArguments) -> Result<SignatoryKeySet, Error> {
  148. let req: super::RotationRequest = args.into();
  149. self.client
  150. .clone()
  151. .rotate_keyset(with_version_header(tonic::Request::new(req)))
  152. .await
  153. .map(|response| handle_error!(response, keyset).try_into())
  154. .map_err(|e| Error::Custom(e.to_string()))?
  155. }
  156. }