pub fn verify_client(
authorized_keys: &AuthorizedKeys,
nonce: &[u8; 32],
public_key: &[u8; 32],
signature: &[u8; 64],
) -> Result<ClientRole<RoleId>, ClientAuthError>Expand description
Decide a client’s challenge response: the key must be listed, under a
role that may connect as a client, and must have signed nonce.
Returns the connection’s role, as the runtime carries it: the operator,
or an application role as its index in the role table.
nonce must be the one this listener sent the client for this
connection, fresh from a cryptographic random source: a nonce reused
across connections lets a recorded response be replayed.
The role is checked before the signature, so a refused key costs no verification; the answer the client sees is the same failure either way.
use base64::Engine;
use ed25519_dalek::{Signer, SigningKey};
use melin_app::auth::{AuthorizedKeys, NoRoles};
use melin_server_runtime::client_auth::{ClientAuthError, verify_client};
let client = SigningKey::from_bytes(&[7; 32]);
let public_key = client.verifying_key().to_bytes();
let listed = base64::engine::general_purpose::STANDARD.encode(public_key);
let keys = AuthorizedKeys::parse::<NoRoles>(&format!("operator {listed} ops\n")).unwrap();
// The listener sent this nonce; the client signed it.
let nonce = [0x5A; 32];
let signature = client.sign(&nonce).to_bytes();
let role = verify_client(&keys, &nonce, &public_key, &signature).unwrap();
assert!(role.is_operator());
let impostor = SigningKey::from_bytes(&[8; 32]).sign(&nonce).to_bytes();
assert!(matches!(
verify_client(&keys, &nonce, &public_key, &impostor),
Err(ClientAuthError::BadSignature(_))
));