Skip to content
This repository was archived by the owner on May 30, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/gem_auth/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ jsonwebtoken = { workspace = true, optional = true }
[dev-dependencies]
alloy-signer = { workspace = true }
alloy-signer-local = { workspace = true }
primitives = { path = "../primitives", features = ["testkit"] }
7 changes: 1 addition & 6 deletions crates/gem_auth/src/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,7 @@ mod tests {
use super::*;
use alloy_signer::SignerSync;
use alloy_signer_local::PrivateKeySigner;
use primitives::{AuthNonce, Chain};

const TEST_PRIVATE_KEY: [u8; 32] = [
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c,
0x1d, 0x1e, 0x1f, 0x20,
];
use primitives::{AuthNonce, Chain, testkit::signer_mock::TEST_PRIVATE_KEY};

fn sign_auth_message(auth_message: &AuthMessage, signer: &PrivateKeySigner) -> String {
let message = serde_json::to_string(auth_message).unwrap();
Expand Down
61 changes: 58 additions & 3 deletions gemstone/src/auth.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
use crate::GemstoneError;
use gem_auth::create_auth_hash;
use primitives::{AuthMessage, AuthNonce, Chain};
use primitives::{AuthMessage, AuthNonce, Chain, hex::encode_with_0x};
use signer::Signer;
use zeroize::Zeroizing;

const AUTH_SIGNING_BYTES_LENGTH: usize = 32;

pub type GemAuthNonce = AuthNonce;

Expand All @@ -16,9 +21,9 @@ pub struct GemAuthMessage {
}

#[uniffi::export]
pub fn create_auth_message(chain: Chain, address: &str, auth_nonce: GemAuthNonce) -> GemAuthMessage {
pub fn create_auth_message(address: &str, auth_nonce: GemAuthNonce) -> GemAuthMessage {
let auth_message = AuthMessage {
chain,
chain: Chain::Ethereum,
address: address.to_string(),
auth_nonce,
};
Expand All @@ -28,3 +33,53 @@ pub fn create_auth_message(chain: Chain, address: &str, auth_nonce: GemAuthNonce
hash: data.hash.to_vec(),
}
}

#[uniffi::export]
pub fn sign_auth_message_hash(hash: Vec<u8>, private_key: Vec<u8>) -> Result<String, GemstoneError> {
let private_key = Zeroizing::new(private_key);
if hash.len() != AUTH_SIGNING_BYTES_LENGTH || private_key.len() != AUTH_SIGNING_BYTES_LENGTH {
Comment thread
0xh3rman marked this conversation as resolved.
return Err(GemstoneError::from("Invalid auth message signing input"));
}
let signature = Signer::sign_ethereum_digest(&hash, private_key.as_slice())?;
Ok(encode_with_0x(&signature))
}
Comment thread
0xh3rman marked this conversation as resolved.

#[cfg(test)]
mod tests {
use super::*;
use alloy_primitives::{Address, keccak256};
use gem_auth::verify_auth_signature;
use primitives::testkit::signer_mock::TEST_PRIVATE_KEY;
use signer::secp256k1_uncompressed_public_key;

#[test]
fn test_sign_auth_message_hash() {
let address = address_from_private_key(&TEST_PRIVATE_KEY);
let auth_nonce = AuthNonce {
nonce: "test-nonce-123".to_string(),
timestamp: 1734100000,
};
let auth_message = AuthMessage {
chain: Chain::Ethereum,
address: address.clone(),
auth_nonce: auth_nonce.clone(),
};
let message = create_auth_message(&address, auth_nonce);

let signature = sign_auth_message_hash(message.hash, TEST_PRIVATE_KEY.to_vec()).unwrap();

assert!(verify_auth_signature(&auth_message, &signature));
}

#[test]
fn test_sign_auth_message_hash_rejects_invalid_input_length() {
assert!(sign_auth_message_hash(vec![0; 31], TEST_PRIVATE_KEY.to_vec()).is_err());
assert!(sign_auth_message_hash(vec![0; 32], vec![0; 31]).is_err());
}

fn address_from_private_key(private_key: &[u8]) -> String {
let public_key = secp256k1_uncompressed_public_key(private_key).unwrap();
let hash = keccak256(&public_key[1..]);
Address::from_slice(&hash[12..]).to_checksum(None)
}
}
Loading