openmls/ciphersuite/signable.rs
1//! This module defines traits used for signing and verifying
2//! structs from the MLS protocol spec.
3//!
4//! # Type-Enforced Verification
5//!
6//! This module contains four traits, each describing the property they enable
7//! upon implementation: [`Signable`], [`SignedStruct`], [`Verifiable`] and
8//! [`VerifiedStruct`].
9//!
10//! Each trait represents the state of a struct in a sender-receiver flow with
11//! the following transitions.
12//!
13//! * the signer creates an instance of a struct that implements [`Signable`]
14//! * the signer signs it, consuming the [`Signable`] struct and producing a [`SignedStruct`]
15//! * the signer serializes the struct and sends it to the verifier
16//! * the verifier deserializes the byte-string into a struct implementing [`Verifiable`]
17//! * the verifier verifies the struct, consuming the [`Verifiable`] struct and producing a [`VerifiedStruct`]
18//!
19//! Using this process, we can ensure that only structs implementing
20//! [`SignedStruct`] are sent over the wire and only structs implementing
21//! [`VerifiedStruct`] are used on the verifier side as input for further
22//! processing functions.
23//!
24//! For the type-safety to work, it is important that [`Signable`] and
25//! [`SignedStruct`] are implemented by distinct structs. The same goes for
26//! [`Verifiable`] and [`VerifiedStruct`]. In addition, only the
27//! [`SignedStruct`] should implement the [`tls_codec::Serialize`] trait.
28//! Similarly, only the [`Verifiable`] struct should implement the
29//! [`tls_codec::Deserialize`] trait.
30
31use openmls_traits::{crypto::OpenMlsCrypto, signatures::Signer};
32use thiserror::Error;
33use tls_codec::Serialize;
34
35use crate::ciphersuite::{OpenMlsSignaturePublicKey, SignContent, Signature};
36
37/// Signature generation and verification errors.
38/// The only information relayed with this error is whether the signature
39/// verification or generation failed.
40#[derive(Error, Debug, PartialEq, Eq, Clone)]
41pub enum SignatureError {
42 /// Signature verification failed
43 #[error("Signature verification failed.")]
44 VerificationError,
45 /// Signature generation failed
46 #[error("Signature generation failed.")]
47 SigningError,
48}
49
50/// This trait must be implemented by all structs that contain a self-signature.
51pub trait SignedStruct<T> {
52 /// Build a signed struct version from the payload struct.
53 fn from_payload(payload: T, signature: Signature, serialized_payload: Vec<u8>) -> Self;
54}
55
56/// The `Signable` trait is implemented by all struct that are being signed.
57/// The implementation has to provide the `unsigned_payload` function.
58pub trait Signable: Sized {
59 /// The type of the object once it's signed.
60 type SignedOutput;
61
62 /// Return the unsigned, serialized payload that should be signed.
63 fn unsigned_payload(&self) -> Result<Vec<u8>, tls_codec::Error>;
64
65 /// Return the string label used for labeled signing.
66 fn label(&self) -> &str;
67
68 /// Sign the payload with the given `private_key`.
69 ///
70 /// Returns a `Signature`.
71 fn sign(self, signer: &impl Signer) -> Result<Self::SignedOutput, SignatureError>
72 where
73 Self::SignedOutput: SignedStruct<Self>,
74 {
75 let payload = self
76 .unsigned_payload()
77 .map_err(|_| SignatureError::SigningError)?;
78 let payload_sign_content =
79 match SignContent::new(self.label(), payload.clone().into()).tls_serialize_detached() {
80 Ok(p) => p,
81 Err(e) => {
82 log::error!("Serializing SignContent failed, {e:?}");
83 return Err(SignatureError::SigningError);
84 }
85 };
86 let signature = signer
87 .sign(&payload_sign_content)
88 .map_err(|_| SignatureError::SigningError)?;
89
90 Ok(Self::SignedOutput::from_payload(
91 self,
92 signature.into(),
93 payload,
94 ))
95 }
96}
97
98/// This marker trait must be implemented by all structs that contain a verified
99/// self-signature.
100pub trait VerifiedStruct {}
101
102/// The verifiable trait must be implemented by any struct that is signed with
103/// a credential. The actual `verify` method is provided.
104/// The `unsigned_payload` and `signature` functions have to be implemented for
105/// each struct, returning the serialized payload and the signature respectively.
106///
107/// Note that `Verifiable` should not be implemented on the same struct as
108/// `Signable`. If this appears to be necessary, it is probably a sign that the
109/// struct implementing them aren't well defined. Not that both traits define an
110/// `unsigned_payload` function.
111pub trait Verifiable: Sized {
112 /// The type used for representing the verified data. Must implement the marker trait
113 /// [`VerifiedStruct`].
114 type VerifiedStruct: VerifiedStruct;
115
116 /// Return the unsigned, serialized payload that should be verified.
117 fn unsigned_payload(&self) -> Result<Vec<u8>, tls_codec::Error>;
118
119 /// A reference to the signature to be verified.
120 fn signature(&self) -> &Signature;
121
122 /// Return the string label used for labeled verification.
123 fn label(&self) -> &str;
124
125 /// Verifies the payload against the given `credential`.
126 /// Usually this is implemented by first checking that `self.verify_no_out()`
127 /// does not return an error, and then converting the value into
128 /// `Self::VerifiedStruct`.
129 ///
130 /// Returns `Ok(Self::VerifiedOutput)` if the signature is valid and
131 /// `CredentialError::InvalidSignature` otherwise.
132 fn verify(
133 self,
134 crypto: &impl OpenMlsCrypto,
135 pk: &OpenMlsSignaturePublicKey,
136 ) -> Result<Self::VerifiedStruct, SignatureError>;
137
138 /// Verifies the payload against the given public key.
139 /// The signature is fetched via the [`Verifiable::signature()`] function and
140 /// the payload via [`Verifiable::unsigned_payload()`].
141 ///
142 /// Returns `Ok(())` if the signature is valid and
143 /// [`SignatureError::VerificationError`] otherwise.
144 fn verify_no_out(
145 &self,
146 crypto: &impl OpenMlsCrypto,
147 pk: &OpenMlsSignaturePublicKey,
148 ) -> Result<(), SignatureError> {
149 let payload = self
150 .unsigned_payload()
151 .map_err(|_| SignatureError::VerificationError)?;
152 let sign_content = SignContent::new(self.label(), payload.into());
153 let payload = match sign_content.tls_serialize_detached() {
154 Ok(p) => p,
155 Err(e) => {
156 log::error!("Serializing SignContent failed, {e:?}");
157 return Err(SignatureError::VerificationError);
158 }
159 };
160 // https://validation.openmls.tech/#valn1301
161 crypto
162 .verify_signature(
163 pk.signature_scheme(),
164 &payload,
165 pk.as_slice(),
166 self.signature().value(),
167 )
168 .map_err(|_| SignatureError::VerificationError)
169 }
170}