For verifying signatures within a contract, the [Schnorrkel library](https://docs.rs/schnorrkel/latest/schnorrkel/) is utilized. This library provides support for Schnorr signatures using the Ristretto group, offering a robust framework for cryptographic operations.
Imagine the format of the data to be signed as follows:
```rust
pub struct SignatureData {
pub key: ActorId,
pub duration: u64,
pub allowed_actions: Vec<ActionsForSession>,
}
```
This structure includes a `key`, representing the unique identifier of an actor (e.g., a user or a system component), a `duration` indicating the validity period of the signature, and `allowed_actions`, a list of actions that the signer is permitted to perform within the session.
The contract receives the fields specified in the structure (`key`, `duration`, `allowed_actions`), the `signature` itself (a byte array), public key of the signer (`pub_key`)through a function like:
```rust
fn verify_signature(
key: ActorId,
duration: u64,
allowed_actions: Vec<ActionsForSession>,
signature: Vec<u8>,
) {}
```
To verify the signature, it's necessary first to reconstruct the payload that the user signed.
The user’s data is encapsulated within a struct, `SignatureData`, which includes essential fields such as `key`, `duration`, and `allowed_actions`. This struct is then serialized (encoded) into a byte format.
```rust
let message = SignatureData {
key,
duration,
allowed_actions,
}.encode();
```
Following the serialization, the encoded message is framed with predefined prefixes and postfixes, <Bytes> and </Bytes>, respectively. This is essential since the operation is performed on the client side, requiring a consistent format for both the serialization and subsequent deserialization processes.
```rust
let mut combined_message = Vec::new();
combined_message.extend_from_slice(b"<Bytes>");
combined_message.extend_from_slice(&message);
combined_message.extend_from_slice(b"</Bytes>");
```
*Signature Verification*:
```rust
const SIGNING_CONTEXT: &[u8] = b"substrate";
fn verify_signature(
key: ActorId,
duration: u64,
allowed_actions: Vec<ActionsForSession>,
signature: Vec<u8>,
pub_key: ActorId
) {
...
// The signature and public key are converted from their byte representations.
let signature = schnorrkel::Signature::from_bytes(&signature).expect("Invalid signature format");
let pubkey_bytes: [u8; 32] = pub_key.into();
let pub_key = schnorrkel::PublicKey::from_bytes(&pubkey_bytes).expect("Invalid public key format");
// Verification against the signing context, ensuring the message integrity.
pub_key.verify_simple(
SIGNING_CONTEXT,
&combined_message,
&signature
).expect("Signature verification failed");
}
```