//! User related things use super::*; use cryptohelpers::{ rsa::{ RsaPublicKey, Signature, }, sha256::{self, Sha256Hash}, }; use std::borrow::Borrow; use std::hash::{Hasher, Hash}; id_type!(UserID; "A unique user iD"); /// The salt added to to the user ID hash to be signed by the user's private key. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] pub struct UserSalt([u8; 16]); impl UserSalt { /// Generate a new random salt. pub fn generate() -> Self { let mut ar = [0u8; 16]; getrandom::getrandom(&mut ar[..]).expect("rng fatal"); Self(ar) } } impl UserID { /// SHA256 hash this ID with a salt pub fn hash_with_salt(&self, salt: &UserSalt) -> Sha256Hash { sha256::compute_slice_iter(iter![&self.0.as_bytes()[..], &salt.0[..]]) } /// Generate a new salt and then return that salt and this ID hashed with that new salt. /// /// This salt should be pub fn generate_hash(&self) -> (UserSalt, Sha256Hash) { let salt = UserSalt::generate(); let hash = self.hash_with_salt(&salt); (salt, hash) } } /// A user identifier. /// /// Contains the user's unique ID, their public key(s), and a valid signature of the sha256 hash of the user's ID + a random salt. /// /// # Hash /// This type hashes to its unique ID, and also borrows to its unique ID. #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct User { /// The user's unique ID. id: UserID, /// A optional set of identifiers given by the user. The user must be trusted to set/edit this value. ident: post::Ident, /// The public keys associated with this user. /// /// # Trust /// Each public key must have a corresponding signature in its complemental entry in `id_sig` to be considered trusted. /// A user with no trusted public keys can be anyone or multiple people. This is not *disallowed* usually but should be discouraged. /// /// Users are only considered trusted if they have at least one trusted public key. pubkey: Vec, /// This vector contains the complemental signature (and salt used with `id` to produce the signed hash) to the public keys in `pubkey`. Each element of `pubkey` must have a complemental element in this vector. /// /// # Trusted public keys /// `None` values for this are signatures that have not yet been produces for a given salt, and do not count as complete. Public keys in `pubkey` that do not have a corresponding `Some` signature value in this field should not be trusted. id_sig: Vec<(UserSalt, Option)>, } impl Hash for User { fn hash(&self, state: &mut H) { self.id.hash(state) } } impl Borrow for User { fn borrow(&self) -> &UserID { &self.id } } impl User { /// Is this user a trusted user? /// /// Does this user have at least one trusted public key (they have produced a valid signature specified in `id_sig`). pub fn is_trusted(&self) -> eyre::Result { for (i, (key, (salt, sig))) in (0..).zip(self.pubkey.iter().zip(self.id_sig.iter())) { if let Some(sig) = sig { let hash = self.id.hash_with_salt(salt); if sig.verify_slice(&hash, key) .with_section(move || format!("{:?}", key).header("Public key was")) .with_section(move || format!("{:?}", sig).header("Signature was")) .with_section(move || format!("{:?}", salt).header("Salt was")) .with_section(move || format!("{:?}", hash).header("Hash was")) .with_note(|| i.to_string().header("For pubkey")) .with_note(|| format!("{:?} ({:?})", self.id, self.ident).header("For user")) .with_warning(|| "This could indicate key or signature corruption. This key or signature may need to be removed.") .with_suggestion(|| "If the user is unable to produce a verifyable signature for this public key despite haiving access to the private key, the key may be corrupted and may need to be removed and replaced.") .wrap_err(eyre!("Failed to verify embedded signature of salted+hashed ID to complementary public key"))? { return Ok(true); } } } Ok(false) } }