Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make SignContent struct or Signable trait public #222

Open
mgeisler opened this issue Nov 29, 2024 · 5 comments
Open

Make SignContent struct or Signable trait public #222

mgeisler opened this issue Nov 29, 2024 · 5 comments

Comments

@mgeisler
Copy link
Contributor

Description of feature:

We would like to use the SignWithLabel and VerifyWithLabel functionality from Section 5.1.2 with our data.

Implementation discussion (Optional)

I did this internally with this simple code:

#[derive(Clone, MlsSize, MlsEncode)]
struct SignContent<'a> {
    #[mls_codec(with = "mls_rs_codec::byte_vec")]
    label: Vec<u8>,
    #[mls_codec(with = "mls_rs_codec::byte_vec")]
    content: &'a [u8],
}

impl fmt::Debug for SignContent<'_> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("SignContent")
            .field("label", &mls_rs_core::debug::pretty_bytes(&self.label))
            .field("content", &mls_rs_core::debug::pretty_bytes(self.content))
            .finish()
    }
}

impl SignContent<'_> {
    pub fn new<'a>(label: &'a str, content: &'a [u8]) -> SignContent<'a> {
        SignContent { label: [b"MLS 1.0 ", label.as_bytes()].concat(), content }
    }
}

This is a trimmed down version of the functionality used by the Signable trait.

Do you think we could make either the full trait public, or make a SignContent struct available?

Thanks for considering!

@mgeisler
Copy link
Contributor Author

I made SignContent borrow the label since I found that I only need the struct to stay around for a split second in my use case: I create it and then immediately use it with a sign or a verify method.

@mulmarta
Copy link
Contributor

mulmarta commented Dec 2, 2024

Maybe expose let s = sign_with_label(label, bytes) and verify_with_label(label, bytes, s) would make a better API? Signable is a bit cumbersome and exposing SignContent means the application has to deal with signatures.

(The reason content is owned in SignContent is that Signable::signable_content typically runs mls_encode which creates a vec, an you can't return a reference to locally created variable which would be dropped.)

@mgeisler
Copy link
Contributor Author

mgeisler commented Dec 3, 2024

Maybe expose let s = sign_with_label(label, bytes) and verify_with_label(label, bytes, s) would make a better API?

Yes, that would be a nice and very usable API for me!

Signable is a bit cumbersome and exposing SignContent means the application has to deal with signatures.

(The reason content is owned in SignContent is that Signable::signable_content typically runs mls_encode which creates a vec, an you can't return a reference to locally created variable which would be dropped.)

I saw the Signable trait and I must confess that I'm not sure why this functionality needs so much machinery 😄 But if we can provide two simple functions for others, then I'm more than happy.

I'll put up a PR for this next.

@mgeisler
Copy link
Contributor Author

mgeisler commented Dec 3, 2024

@mulmarta, would you be up for having a new method on CipherSuiteProvider, with a default implementation:

    /// Sign `data` using `secret_key` and `label`.
    ///
    /// This implements [SignWithLabel][1]. The `label` can be used to
    /// distinguish between signatures on the same `data` made in
    /// different contexts.
    ///
    /// [1]: https://www.rfc-editor.org/rfc/rfc9420.html#name-signing
    async fn sign_with_label(
        &self,
        secret_key: &SignatureSecretKey,
        label: &str,
        data: &[u8],
    ) -> Result<Vec<u8>, Self::Error> {
        let sign_content = SignContent::new(label, data).mls_encode_to_vec()?;
        self.sign(&secret_key, &sign_content)
    }

The whole signer module could then be moved to mls-rs-core if we go this way.

Alternatively, I can expose a new top-level function in signer:

    /// Sign `data` using `secret_key` and `label`.
    ///
    /// This implements [SignWithLabel][1]. The `label` can be used to
    /// distinguish between signatures on the same `data` made in
    /// different contexts.
    ///
    /// [1]: https://www.rfc-editor.org/rfc/rfc9420.html#name-signing
    async fn sign_with_label<P: CipherSuiteProvider>(
        signature_provider: &P,
        secret_key: &SignatureSecretKey,
        label: &str,
        data: &[u8],
    ) -> Result<Vec<u8>, MlsError> {

Which approach do you like better? I think the second means moving less code around, so it might fit better with the current layering of mls-rs vs mls-rs-core.

@mulmarta
Copy link
Contributor

Sorry @mgeisler we're a bit under water at the moment. I like approach 1 better, somehow sign-with-label conceptually fits more in the crypto provider part. You could use it without using MLS FWIW.

I don't know why Signable ended up a bit complicated, that's a very very old piece of code. If you have ideas how to simplify, help is welcome :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants