uniauth 2.1.1

Easy-to-use abstraction over authentication
Documentation
//! The daemon RPC API for use by clients.

use crate::{
	any::*,
	error::*,
	requests,
	util::{AsyncReadExtraExt, AsyncWriteExtraExt},
	daemon_path
};

use std::path::Path;

use tokio::{
	io::{AsyncReadExt, AsyncWriteExt},
	net::UnixStream
};

/// Connection to the user's uniauth daemon, used for RPC
/// Connection with the daemon will last as long as this struct exists.
/// Should only be used by an application's client, never a server.
pub struct Daemon {
	sock: UnixStream
}

impl Daemon {
	/// Connect to the default daemon path.
	///
	/// # Arguments
	/// * `service` - Name of this service
	///
	/// # Example
	/// ```
	/// let daemon = Daemon::new("matrix").await?;
	/// ```
	pub async fn new(service: &str) -> Result<Self> {
		Self::connect(service, &daemon_path()).await
	}

	/// Connect to a custom daemon path
	///
	/// # Arguments
	/// * `service` - Name of this service
	/// * `path` - Path to the daemon's unix socket
	///
	/// # Example
	/// ```
	/// let daemon = Daemon::connect("test_thing", "/tmp/test.sock").await?;
	/// ```
	pub async fn connect(service: &str, path: impl AsRef<Path>) -> Result<Self> {
		let mut sock = UnixStream::connect(path).await?;

		sock.write_str(service).await?;

		Ok(Self {
			sock
		})
	}

	/// Authenticate an action for a given service login, returning its signature.
	/// All fields must be known by the server to authenticate with through communication or hardcoding.
	///
	/// # Arguments
	/// * `name` - Name of the service's key to use
	/// * `action` - Action to authenticate. Must be human readable, but also completely deterministic
	/// * `nonce` - Unique random data or atomic counter that is unique to this challenge and never used again
	///
	/// # Example
	/// ```
	/// let nonce = random_data_from_server();
	/// let sig = daemon.authenticate("username", "login", &nonce).await?;
	/// send_signature_to_server(sig);
	/// ```
	pub async fn authenticate(&mut self, name: &str, action: &str, nonce: &[u8]) -> Result<AnySignature> {
		let sock = &mut self.sock;
		sock.write_u8(requests::AUTHENTICATE).await?;
		sock.write_str(name).await?;
		sock.write_str(action).await?;
		sock.write_b8(nonce).await?;

		let status = sock.read_u8().await?;
		Error::from_status(status)?;

		let algo = sock.read_str().await?;
		let bytes = sock.read_b16().await?;

		AnySignature::new(&algo, &bytes)
	}

	/// Get the public key for a given name
	///
	/// # Arguments
	/// * `name` - Name of the key as added with `store`
	///
	/// # Example
	/// ```
	/// let pubkey = daemon.pubkey("username").await?;
	/// send_server_pubkey(pubkey);
	/// // have the server verify a regsiter action now
	/// ```
	pub async fn pubkey(&mut self, name: &str) -> Result<AnyPubkey> {
		let sock = &mut self.sock;
		sock.write_u8(requests::PUBKEY).await?;
		sock.write_str(name).await?;

		let status = sock.read_u8().await?;
		Error::from_status(status)?;

		let algo = sock.read_str().await?;
		let bytes = sock.read_b16().await?;
		AnyPubkey::new(&algo, &bytes)
	}

	/// Store a keypair for a new service login.
	/// Will fail if a keypair already exists.
	///
	/// # Warning
	/// Since the client provides the keypair, this gives it a requirement for extra security.
	/// Keys that are generated and immediately stored should use `generate` instead.
	///
	/// # Arguments
	/// * `name` - Name of the key to be retrieved by `authenticate` or `pubkey`
	/// * `keypair` - The keypair used for signing challenges
	///
	/// # Example
	/// ```
	/// let keypair = import_keypair();
	/// daemon.store("username", keypair).await?;
	/// ```
	pub async fn store(&mut self, name: &str, keypair: AnyKeypair) -> Result<()> {
		let sock = &mut self.sock;
		sock.write_u8(requests::STORE).await?;
		sock.write_str(name).await?;
		sock.write_str(keypair.name()).await?;
		sock.write_b16(keypair.public_bytes()).await?;
		sock.write_b16(keypair.secret_bytes()).await?;

		let status = sock.read_u8().await?;
		Error::from_status(status)
	}

	/// Generate a new keypair and return its public half.
	/// Will fail if a keypair already exists
	///
	/// # Arguments
	/// * `name` - Name of the key to generate
	///
	/// # Example
	/// ```
	/// let pubkey = daemon.generate("username").await?;
	/// pubkey.verify(b"whatever", signature)?;
	/// ```
	pub async fn generate(&mut self, name: &str) -> Result<AnyPubkey> {
		let sock = &mut self.sock;
		sock.write_u8(requests::GENERATE).await?;
		sock.write_str(name).await?;

		let status = sock.read_u8().await?;
		Error::from_status(status)?;

		let algo = sock.read_str().await?;
		let bytes = sock.read_b16().await?;
		AnyPubkey::new(&algo, &bytes)
	}
}