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) -> 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 = match SignContent::new(self.label(), payload.into()).tls_serialize_detached()
79 {
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)
88 .map_err(|_| SignatureError::SigningError)?;
89
90 Ok(Self::SignedOutput::from_payload(self, signature.into()))
91 }
92}
93
94/// This marker trait must be implemented by all structs that contain a verified
95/// self-signature.
96pub trait VerifiedStruct {}
97
98/// The verifiable trait must be implemented by any struct that is signed with
99/// a credential. The actual `verify` method is provided.
100/// The `unsigned_payload` and `signature` functions have to be implemented for
101/// each struct, returning the serialized payload and the signature respectively.
102///
103/// Note that `Verifiable` should not be implemented on the same struct as
104/// `Signable`. If this appears to be necessary, it is probably a sign that the
105/// struct implementing them aren't well defined. Not that both traits define an
106/// `unsigned_payload` function.
107pub trait Verifiable: Sized {
108 /// The type used for representing the verified data. Must implement the marker trait
109 /// [`VerifiedStruct`].
110 type VerifiedStruct: VerifiedStruct;
111
112 /// Return the unsigned, serialized payload that should be verified.
113 fn unsigned_payload(&self) -> Result<Vec<u8>, tls_codec::Error>;
114
115 /// A reference to the signature to be verified.
116 fn signature(&self) -> &Signature;
117
118 /// Return the string label used for labeled verification.
119 fn label(&self) -> &str;
120
121 /// Verifies the payload against the given `credential`.
122 /// Usually this is implemented by first checking that `self.verify_no_out()`
123 /// does not return an error, and then converting the value into
124 /// `Self::VerifiedStruct`.
125 ///
126 /// Returns `Ok(Self::VerifiedOutput)` if the signature is valid and
127 /// `CredentialError::InvalidSignature` otherwise.
128 fn verify(
129 self,
130 crypto: &impl OpenMlsCrypto,
131 pk: &OpenMlsSignaturePublicKey,
132 ) -> Result<Self::VerifiedStruct, SignatureError>;
133
134 /// Verifies the payload against the given public key.
135 /// The signature is fetched via the [`Verifiable::signature()`] function and
136 /// the payload via [`Verifiable::unsigned_payload()`].
137 ///
138 /// Returns `Ok(())` if the signature is valid and
139 /// [`SignatureError::VerificationError`] otherwise.
140 fn verify_no_out(
141 &self,
142 crypto: &impl OpenMlsCrypto,
143 pk: &OpenMlsSignaturePublicKey,
144 ) -> Result<(), SignatureError> {
145 let payload = self
146 .unsigned_payload()
147 .map_err(|_| SignatureError::VerificationError)?;
148 let sign_content = SignContent::new(self.label(), payload.into());
149 let payload = match sign_content.tls_serialize_detached() {
150 Ok(p) => p,
151 Err(e) => {
152 log::error!("Serializing SignContent failed, {:?}", e);
153 return Err(SignatureError::VerificationError);
154 }
155 };
156 // https://validation.openmls.tech/#valn1301
157 crypto
158 .verify_signature(
159 pk.signature_scheme(),
160 &payload,
161 pk.as_slice(),
162 self.signature().value(),
163 )
164 .map_err(|_| SignatureError::VerificationError)
165 }
166}