parent
af3979e6a2
commit
e43dee3322
@ -1,109 +1,14 @@
|
||||
use super::*;
|
||||
use std::collections::HashMap;
|
||||
use cryptohelpers::{
|
||||
rsa::{
|
||||
RsaPublicKey,
|
||||
Signature,
|
||||
},
|
||||
sha256::{self, Sha256Hash},
|
||||
};
|
||||
use post::Post;
|
||||
|
||||
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.
|
||||
#[derive(Debug, PartialEq, Eq, Hash, 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<RsaPublicKey>,
|
||||
|
||||
/// 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<Signature>)>,
|
||||
}
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use super::*;
|
||||
|
||||
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<bool>
|
||||
{
|
||||
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)
|
||||
}
|
||||
}
|
||||
use user::{User, UserID};
|
||||
use post::Post;
|
||||
|
||||
/// Contains all posts
|
||||
#[derive(Debug)]
|
||||
pub struct State
|
||||
{
|
||||
entries: HashMap<User, MaybeVec<Post>>
|
||||
users: HashSet<User>,
|
||||
posts: HashMap<UserID, MaybeVec<Post>>
|
||||
}
|
||||
|
@ -1,3 +1,48 @@
|
||||
use super::*;
|
||||
use maud::{
|
||||
Markup,
|
||||
Render,
|
||||
};
|
||||
use maud::DOCTYPE;
|
||||
|
||||
/// Posts view page template
|
||||
mod view
|
||||
{
|
||||
use super::*;
|
||||
|
||||
/// Head for the view page template
|
||||
pub fn head() -> Markup
|
||||
{
|
||||
html! {
|
||||
//TODO
|
||||
}
|
||||
}
|
||||
|
||||
pub fn body(state: &state::State) -> Markup
|
||||
{
|
||||
html! {
|
||||
//TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline] fn page(head: impl Render, body: impl Render) -> Markup
|
||||
{
|
||||
html! {
|
||||
(DOCTYPE)
|
||||
head {
|
||||
(head)
|
||||
}
|
||||
body {
|
||||
main {
|
||||
(body)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Post view page
|
||||
pub fn view(state: &state::State) -> Markup
|
||||
{
|
||||
page(view::head(), view::body(state))
|
||||
}
|
||||
|
@ -0,0 +1,123 @@
|
||||
//! 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<RsaPublicKey>,
|
||||
|
||||
/// 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<Signature>)>,
|
||||
}
|
||||
|
||||
impl Hash for User {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.id.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Borrow<UserID> 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<bool>
|
||||
{
|
||||
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)
|
||||
}
|
||||
}
|
Loading…
Reference in new issue