rustsignaturesolanaverifynonce

How do I use Rust to verify a solan signature?


I made a signature on solana

solana verify-offchain-signature --signer DR2BCjL1yX5HnuTSYVMeRY5tsHd8ZJZz2vFf5zs5aYop "55f04639-d058-407a-8b38-7df7b20b39a0" 2XP1mHvXaRHzp6ZNSQHMgr2bgDEpHwWkW4fBLCzzWxCkRi99ryyUAbUVFratTjhhw1nQxj4WQhZhxnjA7cEnncBV
Signature is valid

Next, I wrote code in Rust to verify this signature

use std::str::FromStr;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::Signature;

async fn main() -> std::io::Result<()> {
    let nonce= "55f04639-d058-407a-8b38-7df7b20b39a0";
    let pubkey= "DR2BCjL1yX5HnuTSYVMeRY5tsHd8ZJZz2vFf5zs5aYop";
    let signature= "2XP1mHvXaRHzp6ZNSQHMgr2bgDEpHwWkW4fBLCzzWxCkRi99ryyUAbUVFratTjhhw1nQxj4WQhZhxnjA7cEnncBV";

    let pubkey = solana_sdk::pubkey::Pubkey::from_str(pubkey).unwrap();
    let signature = solana_sdk::signature::Signature::from_str(signature).unwrap();

    if signature.verify(pubkey.as_ref(), nonce.as_ref()) {
        println!("✅ Valid");
    } else {
        println!("❌ Invalid");
    }
}

But I get ❌ Invalid, why? What do I have to do to verify it correctly?


Solution

  • It worked for me

    use ed25519_dalek::{VerifyingKey, Signature, Verifier};
    use bs58;
    use thiserror::Error;
    use std::convert::TryInto;
    use byteorder::{LittleEndian, WriteBytesExt};
    
    #[derive(Debug, Error)]
    pub enum AuthError {
        #[error("Base58 decoding error: {0}")]
        DecodeError(#[from] bs58::decode::Error),
        #[error("Invalid public key length")]
        PublicKeyLength,
        #[error("Invalid signature length")]
        SignatureLength,
        #[error("Signature verification failed")]
        VerificationFailed(#[from] ed25519_dalek::SignatureError),
    }
    
    pub fn verify_offchain_signature(
        pubkey_b58: &str,
        sig_b58: &str,
        message: &str,
    ) -> Result<(), AuthError> {
        // decode base58
        let pubkey_vec = bs58::decode(pubkey_b58).into_vec()?;
        let sig_vec    = bs58::decode(sig_b58).into_vec()?;
        
        // vec u8 -> array with normal size
        let pubkey_bytes: [u8; 32] = pubkey_vec
            .try_into()
            .map_err(|_| AuthError::PublicKeyLength)?;
        let sig_bytes:    [u8; 64] = sig_vec
            .try_into()
            .map_err(|_| AuthError::SignatureLength)?;
        
        // create objects for verify
        let verifying_key = VerifyingKey::from_bytes(&pubkey_bytes)
            .map_err(|_| AuthError::PublicKeyLength)?;
        let signature = Signature::from_bytes(&sig_bytes);
        
        // do solana like buffer
        let mut buf = Vec::new();
        // prefix for 2 zeros in the end
        buf.extend_from_slice(b"\xffsolana offchain\x00\x00");  
        // little endian size
        buf.write_u16::<LittleEndian>(message.len() as u16).unwrap();
        // message bytes
        buf.extend_from_slice(message.as_bytes());
    
        // verify wrapped message
        verifying_key
            .verify(&buf, &signature)
            .map_err(AuthError::VerificationFailed)?;
        
        Ok(())
    }
    
    
    fn main() {
        let nonce = "55f04639-d058-407a-8b38-7df7b20b39a0";
        let pubkey = "DR2BCjL1yX5HnuTSYVMeRY5tsHd8ZJZz2vFf5zs5aYop";
        let signature = "2XP1mHvXaRHzp6ZNSQHMgr2bgDEpHwWkW4fBLCzzWxCkRi99ryyUAbUVFratTjhhw1nQxj4WQhZhxnjA7cEnncBV";
        
        match verify_offchain_signature(pubkey, signature, nonce) {
            Ok(()) => println!("Signature is valid!"),
            Err(e) => println!("Verification error: {}", e),
        }
    }