m10_signing/
lib.rs

1//! M10's helper library for signing messages with ED25519 or P256 elliptic-curve signatures
2//!
3//! This library contains a set of wrappers and traits that allow users to easily sign and verify
4//! signatures
5use core::str::FromStr;
6use m10_protos::{prost::Message, sdk};
7use std::fmt;
8use std::sync::Arc;
9
10pub use ed25519::Ed25519;
11pub use p256::P256;
12pub use vault::VaultTransit;
13
14mod ed25519;
15mod p256;
16mod vault;
17pub use crate::vault::extract_public_key;
18
19/// A signed request containing both the serialized and signed payload; and the original message
20#[derive(Default, Clone)]
21pub struct SignedRequest<P: Message> {
22    pub request_envelope: sdk::RequestEnvelope,
23    pub data: P,
24}
25
26impl<P: Message> From<SignedRequest<P>> for sdk::RequestEnvelope {
27    fn from(signed_request: SignedRequest<P>) -> Self {
28        signed_request.request_envelope
29    }
30}
31
32impl<P: Message> AsRef<P> for SignedRequest<P> {
33    fn as_ref(&self) -> &P {
34        &self.data
35    }
36}
37
38#[derive(thiserror::Error, Debug)]
39pub enum SigningError {
40    #[error("internal")]
41    Internal,
42    #[error("malformed signature")]
43    MalFormedSignature,
44    #[error("io error: {0}")]
45    Io(#[from] std::io::Error),
46    #[error("{0}")]
47    KeyRejected(#[from] ring::error::KeyRejected),
48    #[error("key invalid: {0}")]
49    KeyInvalid(String),
50}
51
52/// Internal helper for mapping lower-level errors to SigningError::Internal.
53/// When the "verbose_errors" feature flag is enabled, the helper logs the
54/// underlying error details with the provided context.
55#[inline]
56#[allow(unused_variables)]
57fn internal_error<E: std::fmt::Debug>(err: E, context: &'static str) -> SigningError {
58    #[cfg(feature = "verbose_errors")]
59    {
60        log::error!("{}: {:?}", context, err);
61    }
62    SigningError::Internal
63}
64
65#[macro_export]
66macro_rules! debug_verbose {
67    ($($arg:tt)*) => {
68        #[cfg(feature = "verbose_errors")]
69        {
70            log::debug!($($arg)*);
71        }
72    };
73}
74
75/// A trait repersenting a service or key that can sign a message
76///
77/// Typically this trait would be implemented by a key pair (like in [`KeyPair`]), an HSM, or some secure service like Vault
78#[async_trait::async_trait]
79pub trait Signer: Send + Sync {
80    /// Signs the passed message, and returns the signature.
81    async fn sign(&self, msg: &[u8]) -> Result<Vec<u8>, SigningError>;
82
83    /// Returns the public key associated with the signer
84    fn public_key(&self) -> &[u8];
85
86    /// Returns the signing algorithm used by the signer
87    fn algorithm(&self) -> sdk::signature::Algorithm;
88
89    /// Signs the payload, and returns a signature structure containing the algorithm, public-key, and signature
90    async fn sign_payload(&self, payload: &[u8]) -> Result<sdk::Signature, SigningError> {
91        Ok(sdk::Signature {
92            algorithm: self.algorithm().into(),
93            public_key: self.public_key().into(),
94            signature: self.sign(payload).await?,
95        })
96    }
97
98    /// Signs a [`Message`] and returns a [`SignedRequest`]
99    async fn sign_request<P: Message>(&self, data: P) -> Result<SignedRequest<P>, SigningError> {
100        let payload = data.encode_to_vec();
101        let signature = self.sign_payload(&payload).await?;
102        let request_envelope = sdk::RequestEnvelope {
103            payload,
104            signature: Some(signature),
105        };
106        Ok(SignedRequest {
107            request_envelope,
108            data,
109        })
110    }
111
112    /// Adds an endorsement signatured to a [`sdk::Contract`]
113    async fn endorse(
114        &self,
115        contract: &mut sdk::Contract,
116        ledger_id: String,
117    ) -> Result<(), SigningError> {
118        let public_key = self.public_key();
119        let already_signed = contract
120            .endorsements
121            .iter()
122            .filter_map(|endorsement| endorsement.signature.as_ref())
123            .any(|signature| signature.public_key == public_key);
124        if !already_signed {
125            contract.endorsements.push(sdk::Endorsement {
126                ledger_id,
127                signature: Some(self.sign_payload(&contract.transactions).await?),
128            })
129        }
130        Ok(())
131    }
132}
133
134/// A P256 or ED25519 key pair
135#[derive(serde::Deserialize)]
136#[serde(try_from = "String", into = "String")]
137pub enum KeyPair {
138    P256(P256),
139    Ed25519(Ed25519),
140}
141
142#[async_trait::async_trait]
143impl Signer for KeyPair {
144    async fn sign(&self, msg: &[u8]) -> Result<Vec<u8>, SigningError> {
145        match self {
146            KeyPair::P256(key_pair) => key_pair.sign(msg).await,
147            KeyPair::Ed25519(key_pair) => key_pair.sign(msg).await,
148        }
149    }
150
151    fn public_key(&self) -> &[u8] {
152        match self {
153            KeyPair::P256(key_pair) => key_pair.public_key(),
154            KeyPair::Ed25519(key_pair) => key_pair.public_key(),
155        }
156    }
157
158    fn algorithm(&self) -> sdk::signature::Algorithm {
159        match self {
160            KeyPair::P256(key_pair) => key_pair.algorithm(),
161            KeyPair::Ed25519(key_pair) => key_pair.algorithm(),
162        }
163    }
164}
165
166impl FromStr for KeyPair {
167    type Err = SigningError;
168    fn from_str(key_pair_enc: &str) -> Result<Self, Self::Err> {
169        Ed25519::from_str(key_pair_enc)
170            .map(KeyPair::Ed25519)
171            .or_else(|_| P256::from_str(key_pair_enc).map(KeyPair::P256))
172    }
173}
174
175impl TryFrom<String> for KeyPair {
176    type Error = SigningError;
177    fn try_from(key_pair: String) -> Result<Self, Self::Error> {
178        key_pair.parse()
179    }
180}
181
182impl fmt::Debug for KeyPair {
183    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184        match self {
185            KeyPair::P256(key_pair) => write!(f, "P256({:?})", key_pair.public_key()),
186            KeyPair::Ed25519(key_pair) => write!(f, "Ed25519({:?})", key_pair.public_key()),
187        }
188    }
189}
190
191#[derive(Clone, Debug)]
192pub struct ArcKeyPair(pub Arc<KeyPair>);
193
194impl<'de> serde::Deserialize<'de> for ArcKeyPair {
195    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
196    where
197        D: serde::Deserializer<'de>,
198    {
199        let encoded = String::deserialize(deserializer)?;
200        let keypair = Ed25519::from_keypair(&encoded)
201            .map(KeyPair::Ed25519)
202            .map_err(serde::de::Error::custom)?;
203        Ok(Self(Arc::new(keypair)))
204    }
205}
206
207impl std::ops::Deref for ArcKeyPair {
208    type Target = Arc<KeyPair>;
209    fn deref(&self) -> &Self::Target {
210        &self.0
211    }
212}