ssi_verification_methods_core/signature/
protocol.rs

1use std::borrow::Cow;
2
3use ssi_claims_core::MessageSignatureError;
4use ssi_crypto::algorithm::{SignatureAlgorithmInstance, SignatureAlgorithmType};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
7pub struct WithProtocol<A, P>(pub A, pub P);
8
9impl<A, P> WithProtocol<A, P> {
10    pub fn new(algorithm: A, protocol: P) -> Self {
11        Self(algorithm, protocol)
12    }
13}
14
15impl<A: SignatureAlgorithmType, P: Copy> SignatureAlgorithmType for WithProtocol<A, P> {
16    type Instance = WithProtocol<A::Instance, P>;
17}
18
19impl<I: SignatureAlgorithmInstance, P: Copy> SignatureAlgorithmInstance for WithProtocol<I, P> {
20    type Algorithm = WithProtocol<I::Algorithm, P>;
21
22    fn algorithm(&self) -> Self::Algorithm {
23        WithProtocol(self.0.algorithm(), self.1)
24    }
25}
26
27/// Signature protocol.
28///
29/// Specifies how the client and signer communicates together to produce a
30/// signature. This includes:
31/// - how to encode the message sent to the signer,
32/// - what transformation must be operated on the message by the signer,
33/// - how to encode the signature to send back to the client.
34///
35/// For instance when using the `EthereumPersonalSignature2021` cryptographic
36/// suite the [`EthereumWallet`] protocol applies where:
37/// - the signer (the Ethereum Wallet) must prefix the message with
38///   `\x19Ethereum Signed Message:\n`,
39/// - send back the signature encoded in hexadecimal.
40///
41/// The simplest protocol is described by the unit `()` type, where the raw
42/// message is transmitted to the signer, which must sign it and return the
43/// raw bytes.
44pub trait SignatureProtocol<A>: Copy {
45    fn prepare_message<'b>(&self, bytes: &'b [u8]) -> Cow<'b, [u8]> {
46        Cow::Borrowed(bytes)
47    }
48
49    fn prepare_messages<'b>(&self, bytes: &'b [Vec<u8>]) -> Cow<'b, [Vec<u8>]> {
50        Cow::Borrowed(bytes)
51    }
52
53    fn encode_signature(
54        &self,
55        _algorithm: A,
56        signature: Vec<u8>,
57    ) -> Result<Vec<u8>, MessageSignatureError> {
58        Ok(signature)
59    }
60
61    fn decode_signature<'s>(
62        &self,
63        encoded_signature: &'s [u8],
64    ) -> Result<Cow<'s, [u8]>, InvalidProtocolSignature> {
65        Ok(Cow::Borrowed(encoded_signature))
66    }
67}
68
69#[derive(Debug, thiserror::Error)]
70#[error("invalid protocol signature")]
71pub struct InvalidProtocolSignature;
72
73impl<A> SignatureProtocol<A> for () {}
74
75/// Base58Btc Multibase protocol.
76///
77/// The signer must sent back the signature encoded with [multibase][1] using
78/// the `Base58Btc` base.
79///
80/// [1]: <https://github.com/multiformats/multibase>
81#[derive(Debug, Clone, Copy)]
82pub struct Base58BtcMultibase;
83
84impl Base58BtcMultibase {
85    /// Encode the signature with multibase using the `Base58Btc` base as
86    /// required by this protocol.
87    pub fn encode_signature(signature: &[u8]) -> Vec<u8> {
88        multibase::encode(multibase::Base::Base58Btc, signature).into_bytes()
89    }
90
91    pub fn decode_signature(encoded_signature: &[u8]) -> Result<Vec<u8>, InvalidProtocolSignature> {
92        let encoded_signature =
93            std::str::from_utf8(encoded_signature).map_err(|_| InvalidProtocolSignature)?;
94        let (base, signature) =
95            multibase::decode(encoded_signature).map_err(|_| InvalidProtocolSignature)?;
96        if base == multibase::Base::Base58Btc {
97            Ok(signature)
98        } else {
99            Err(InvalidProtocolSignature)
100        }
101    }
102}
103
104impl<A> SignatureProtocol<A> for Base58BtcMultibase {
105    fn encode_signature(
106        &self,
107        _algorithm: A,
108        signature: Vec<u8>,
109    ) -> Result<Vec<u8>, MessageSignatureError> {
110        Ok(Self::encode_signature(&signature))
111    }
112
113    fn decode_signature<'s>(
114        &self,
115        encoded_signature: &'s [u8],
116    ) -> Result<Cow<'s, [u8]>, InvalidProtocolSignature> {
117        Self::decode_signature(encoded_signature).map(Cow::Owned)
118    }
119}
120
121/// Base58Btc protocol.
122///
123/// The signer must sent back the signature encoded in base58 (bitcoin
124/// alphabet).
125#[derive(Debug, Clone, Copy)]
126pub struct Base58Btc;
127
128impl Base58Btc {
129    /// Encode the signature in base58 (bitcoin alphabet) as required by this
130    /// protocol.
131    pub fn encode_signature(signature: &[u8]) -> Vec<u8> {
132        bs58::encode(signature).into_vec()
133    }
134
135    pub fn decode_signature(encoded_signature: &[u8]) -> Result<Vec<u8>, InvalidProtocolSignature> {
136        bs58::decode(encoded_signature)
137            .into_vec()
138            .map_err(|_| InvalidProtocolSignature)
139    }
140}
141
142impl<A> SignatureProtocol<A> for Base58Btc {
143    /// Encode the signature in base58 (bitcoin alphabet) as required by this
144    /// protocol.
145    fn encode_signature(
146        &self,
147        _algorithm: A,
148        signature: Vec<u8>,
149    ) -> Result<Vec<u8>, MessageSignatureError> {
150        Ok(Self::encode_signature(&signature))
151    }
152
153    fn decode_signature<'s>(
154        &self,
155        encoded_signature: &'s [u8],
156    ) -> Result<Cow<'s, [u8]>, InvalidProtocolSignature> {
157        Self::decode_signature(encoded_signature).map(Cow::Owned)
158    }
159}
160
161/// Ethereum Wallet protocol.
162///
163/// Used in combination with the `EthereumPersonalSignature2021` cryptographic
164/// suite. The signer (the Ethereum Wallet) must prefix the message with
165/// `\x19Ethereum Signed Message:\n` followed by the byte length of the message
166/// and send back the signature encoded in hexadecimal, with a `0x` prefix.
167/// The recovery ID in the signature must start at 27 instead of 0.
168#[derive(Debug, Clone, Copy)]
169pub struct EthereumWallet;
170
171impl EthereumWallet {
172    pub fn prepare_message(bytes: &[u8]) -> Vec<u8> {
173        let mut result = format!("\x19Ethereum Signed Message:\n{}", bytes.len()).into_bytes();
174        result.extend_from_slice(bytes);
175        result
176    }
177
178    pub fn encode_signature(signature: &[u8]) -> Vec<u8> {
179        assert_eq!(signature.len(), 65);
180        let mut result = Vec::new();
181        result.extend_from_slice(b"0x");
182        result.resize(132, 0);
183
184        // Encode without the recovery ID.
185        hex::encode_to_slice(&signature[..64], &mut result[2..130]).unwrap();
186
187        // Encode the recovery ID, offset by 27.
188        let rec_id = signature[64] + 27;
189        hex::encode_to_slice(std::slice::from_ref(&rec_id), &mut result[130..]).unwrap();
190
191        // Send back the result.
192        result
193    }
194
195    pub fn decode_signature(encoded_signature: &[u8]) -> Result<Vec<u8>, InvalidProtocolSignature> {
196        let hex = encoded_signature
197            .strip_prefix(b"0x")
198            .ok_or(InvalidProtocolSignature)?;
199
200        let mut signature = hex::decode(hex).map_err(|_| InvalidProtocolSignature)?;
201        signature[64] -= 27; // Offset the recovery ID by -27.
202
203        Ok(signature)
204    }
205}
206
207impl<A> SignatureProtocol<A> for EthereumWallet {
208    fn prepare_message<'b>(&self, bytes: &'b [u8]) -> Cow<'b, [u8]> {
209        Cow::Owned(Self::prepare_message(bytes))
210    }
211
212    fn encode_signature(
213        &self,
214        _algorithm: A,
215        signature: Vec<u8>,
216    ) -> Result<Vec<u8>, MessageSignatureError> {
217        Ok(Self::encode_signature(&signature))
218    }
219
220    fn decode_signature<'s>(
221        &self,
222        encoded_signature: &'s [u8],
223    ) -> Result<Cow<'s, [u8]>, InvalidProtocolSignature> {
224        Self::decode_signature(encoded_signature).map(Cow::Owned)
225    }
226}