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}