Skip to content

Commit

Permalink
Verify signature function (#1290)
Browse files Browse the repository at this point in the history
* verify signature function

* prove it works

* lint
  • Loading branch information
codabrink authored Nov 19, 2024
1 parent 46e3bc7 commit 6cb659b
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 26 deletions.
45 changes: 43 additions & 2 deletions bindings_ffi/src/mls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -367,9 +367,28 @@ impl FfiXmtpClient {

pub fn sign_with_installation_key(&self, text: &str) -> Result<Vec<u8>, GenericError> {
let inner = self.inner_client.as_ref();
let context = inner.context().public_sign(text)?;
Ok(inner.context().public_sign(text)?)
}

pub fn verify_signed_with_installation_key(
&self,
signature_text: &str,
signature_bytes: Vec<u8>,
) -> Result<(), GenericError> {
let signature_bytes: [u8; 64] =
signature_bytes
.try_into()
.map_err(|v: Vec<u8>| GenericError::Generic {
err: format!(
"signature_bytes is not 64 bytes long. (Actual size: {})",
v.len()
),
})?;

Ok(context)
let inner = self.inner_client.as_ref();
Ok(inner
.context()
.public_verify(signature_text, &signature_bytes)?)
}
}

Expand Down Expand Up @@ -3681,6 +3700,28 @@ mod tests {
assert!(results_4.is_ok());
}

#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
async fn test_sign_and_verify() {
let signature_text = "Hello there.";

let client = new_test_client().await;
let signature_bytes = client.sign_with_installation_key(signature_text).unwrap();

// check if verification works
let result =
client.verify_signed_with_installation_key(signature_text, signature_bytes.clone());
assert!(result.is_ok());

// different text should result in an error.
let result = client.verify_signed_with_installation_key("Hello here.", signature_bytes);
assert!(result.is_err());

// different bytes should result in an error
let signature_bytes = vec![0; 64];
let result = client.verify_signed_with_installation_key(signature_text, signature_bytes);
assert!(result.is_err());
}

#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
async fn test_revoke_all_installations() {
let wallet = xmtp_cryptography::utils::LocalWallet::new(&mut rng());
Expand Down
9 changes: 3 additions & 6 deletions xmtp_cryptography/src/basic_credential.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ pub trait CredentialSign<SP = private::NotSpecialized> {
/// the hashed context this credential signature takes place in
type Error;

fn credential_sign<T: SigningContextProvider, S: AsRef<str>>(
fn credential_sign<T: SigningContextProvider>(
&self,
text: S,
text: impl AsRef<str>,
) -> Result<Vec<u8>, Self::Error>;
}

Expand All @@ -59,12 +59,9 @@ pub trait SigningContextProvider {

/// Verify a credential signature with its public key
pub trait CredentialVerify<SP = private::NotSpecialized> {
/// the hashed context this credential signature verification takes place in
/// if this is not defined, the context will be empty
const CONTEXT: &[u8] = b"";
type Error;

fn credential_verify(
fn credential_verify<T: SigningContextProvider>(
&self,
signature_text: impl AsRef<str>,
signature_bytes: &[u8; 64],
Expand Down
9 changes: 4 additions & 5 deletions xmtp_id/src/associations/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ pub struct PublicContext;
impl CredentialSign<InboxIdInstallationCredential> for XmtpInstallationCredential {
type Error = SignatureError;

fn credential_sign<T: SigningContextProvider, S: AsRef<str>>(
fn credential_sign<T: SigningContextProvider>(
&self,
text: S,
text: impl AsRef<str>,
) -> Result<Vec<u8>, Self::Error> {
let mut prehashed: Sha512 = Sha512::new();
prehashed.update(text.as_ref());
Expand All @@ -78,18 +78,17 @@ impl CredentialSign<InboxIdInstallationCredential> for XmtpInstallationCredentia
}

impl CredentialVerify<InboxIdInstallationCredential> for ed25519_dalek::VerifyingKey {
const CONTEXT: &[u8] = crate::constants::INSTALLATION_KEY_SIGNATURE_CONTEXT;
type Error = SignatureError;

fn credential_verify(
fn credential_verify<T: SigningContextProvider>(
&self,
signature_text: impl AsRef<str>,
signature_bytes: &[u8; 64],
) -> Result<(), Self::Error> {
let signature = Signature::from_bytes(signature_bytes);
let mut prehashed = Sha512::new();
prehashed.update(signature_text.as_ref());
self.verify_prehashed(prehashed, Some(Self::CONTEXT), &signature)?;
self.verify_prehashed(prehashed, Some(T::context()), &signature)?;
Ok(())
}
}
Expand Down
2 changes: 1 addition & 1 deletion xmtp_id/src/associations/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ pub async fn add_installation_key_signature(
) {
let signature_text = signature_request.signature_text();
let sig = installation_key
.credential_sign::<InstallationKeyContext, _>(signature_text)
.credential_sign::<InstallationKeyContext>(signature_text)
.unwrap();

let unverified_sig =
Expand Down
9 changes: 6 additions & 3 deletions xmtp_id/src/associations/verified_signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use xmtp_proto::xmtp::message_contents::SignedPublicKey as LegacySignedPublicKey
use crate::scw_verifier::SmartContractSignatureVerifier;

use super::{
to_lower_s, AccountId, MemberIdentifier, SignatureError, SignatureKind,
to_lower_s, AccountId, InstallationKeyContext, MemberIdentifier, SignatureError, SignatureKind,
ValidatedLegacySignedPublicKey,
};

Expand Down Expand Up @@ -88,7 +88,10 @@ impl VerifiedSignature {
signature_bytes: &[u8],
verifying_key: ed25519_dalek::VerifyingKey,
) -> Result<Self, SignatureError> {
verifying_key.credential_verify(signature_text, signature_bytes.try_into()?)?;
verifying_key.credential_verify::<InstallationKeyContext>(
signature_text,
signature_bytes.try_into()?,
)?;
Ok(Self::new(
MemberIdentifier::Installation(verifying_key.as_bytes().to_vec()),
SignatureKind::InstallationKey,
Expand Down Expand Up @@ -223,7 +226,7 @@ mod tests {
let verifying_key = key.verifying_key();
let signature_text = "test signature text";
let sig = key
.credential_sign::<InstallationKeyContext, _>(signature_text)
.credential_sign::<InstallationKeyContext>(signature_text)
.unwrap();

let verified_sig =
Expand Down
11 changes: 10 additions & 1 deletion xmtp_mls/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,9 +199,18 @@ impl XmtpMlsLocalContext {
self.identity.signature_request()
}

pub fn public_sign<Text: AsRef<str>>(&self, text: Text) -> Result<Vec<u8>, IdentityError> {
pub fn public_sign(&self, text: impl AsRef<str>) -> Result<Vec<u8>, IdentityError> {
self.identity.sign_with_public_context(text)
}

pub fn public_verify(
&self,
signature_text: impl AsRef<str>,
signature_bytes: &[u8; 64],
) -> Result<(), IdentityError> {
self.identity
.verify_with_public_context(signature_text, signature_bytes)
}
}

impl<ApiClient, V> Client<ApiClient, V>
Expand Down
27 changes: 19 additions & 8 deletions xmtp_mls/src/identity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use prost::Message;
use thiserror::Error;
use tracing::debug;
use tracing::info;
use xmtp_cryptography::{CredentialSign, XmtpInstallationCredential};
use xmtp_cryptography::{CredentialSign, CredentialVerify, XmtpInstallationCredential};
use xmtp_id::associations::unverified::UnverifiedSignature;
use xmtp_id::associations::{AssociationError, InstallationKeyContext, PublicContext};
use xmtp_id::scw_verifier::SmartContractSignatureVerifier;
Expand Down Expand Up @@ -262,7 +262,7 @@ impl Identity {
.build();

let signature = installation_keys
.credential_sign::<InstallationKeyContext, _>(signature_request.signature_text())?;
.credential_sign::<InstallationKeyContext>(signature_request.signature_text())?;
signature_request
.add_signature(
UnverifiedSignature::new_installation_key(
Expand Down Expand Up @@ -306,7 +306,7 @@ impl Identity {
.build();

let sig = installation_keys
.credential_sign::<InstallationKeyContext, _>(signature_request.signature_text())?;
.credential_sign::<InstallationKeyContext>(signature_request.signature_text())?;

signature_request
.add_signature(
Expand Down Expand Up @@ -363,7 +363,7 @@ impl Identity {
.build();

let sig = installation_keys
.credential_sign::<InstallationKeyContext, _>(signature_request.signature_text())?;
.credential_sign::<InstallationKeyContext>(signature_request.signature_text())?;
// We can pre-sign the request with an installation key signature, since we have access to the key
signature_request
.add_signature(
Expand Down Expand Up @@ -416,16 +416,27 @@ impl Identity {
text: Text,
) -> Result<Vec<u8>, IdentityError> {
self.installation_keys
.credential_sign::<InstallationKeyContext, _>(text)
.credential_sign::<InstallationKeyContext>(text)
.map_err(Into::into)
}

pub fn sign_with_public_context<Text: AsRef<str>>(
pub fn sign_with_public_context(
&self,
text: Text,
text: impl AsRef<str>,
) -> Result<Vec<u8>, IdentityError> {
self.installation_keys
.credential_sign::<PublicContext, _>(text)
.credential_sign::<PublicContext>(text)
.map_err(Into::into)
}

pub fn verify_with_public_context(
&self,
signature_text: impl AsRef<str>,
signature_bytes: &[u8; 64],
) -> Result<(), IdentityError> {
self.installation_keys
.verifying_key()
.credential_verify::<PublicContext>(signature_text, signature_bytes)
.map_err(Into::into)
}

Expand Down

0 comments on commit 6cb659b

Please sign in to comment.