parent
e43dee3322
commit
31ecef8954
@ -0,0 +1,261 @@
|
||||
//! Mnemonic hashes, incl. tripcode.
|
||||
use super::*;
|
||||
use std::borrow::Borrow;
|
||||
use khash::ctx::Context;
|
||||
use cryptohelpers::sha256::{self, Sha256Hash};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::cmp::Ordering;
|
||||
use std::str;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
/// A newtype tripcode string
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||
pub struct Tripcode(String);
|
||||
|
||||
lazy_static!{
|
||||
static ref CONTEXT: Context = Context::new(defaults::TRIPCODE_ALGO, defaults::TRIPCODE_SALT);
|
||||
}
|
||||
|
||||
impl Tripcode
|
||||
{
|
||||
/// Generate a tripcode from this string.
|
||||
pub fn generate(from: impl AsRef<[u8]>) -> Result<Self, khash::error::Error>
|
||||
{
|
||||
khash::generate(&CONTEXT, from).map(Self)
|
||||
}
|
||||
/// Create a tripcode that *is* this string
|
||||
#[inline] pub fn special(string: String) -> Self
|
||||
{
|
||||
Self(string)
|
||||
}
|
||||
/// As a string
|
||||
#[inline] pub fn as_str(&self) -> &str
|
||||
{
|
||||
&self.0
|
||||
}
|
||||
/// Consume into regular string
|
||||
#[inline] pub fn into_inner(self) -> String
|
||||
{
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Tripcode> for String
|
||||
{
|
||||
#[inline] fn from(from: Tripcode) -> Self
|
||||
{
|
||||
from.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Tripcode
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
||||
{
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for Tripcode
|
||||
{
|
||||
fn as_ref(&self) -> &str
|
||||
{
|
||||
&self.0[..]
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<String> for Tripcode
|
||||
{
|
||||
fn borrow(&self) -> &String
|
||||
{
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
use once_cell::sync::OnceCell;
|
||||
|
||||
/// A mnemonic base64 hash
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MnemonicHash(Sha256Hash, OnceCell<String>);
|
||||
|
||||
impl Ord for MnemonicHash
|
||||
{
|
||||
fn cmp(&self, other: &Self) -> Ordering
|
||||
{
|
||||
self.0.cmp(&other.0)
|
||||
}
|
||||
}
|
||||
impl PartialOrd for MnemonicHash
|
||||
{
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
self.0.partial_cmp(&other.0)
|
||||
}
|
||||
}
|
||||
impl Eq for MnemonicHash{}
|
||||
impl PartialEq for MnemonicHash
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool
|
||||
{
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
impl Hash for MnemonicHash {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.0.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl str::FromStr for MnemonicHash
|
||||
{
|
||||
type Err = std::convert::Infallible;
|
||||
|
||||
#[inline] fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Self::from_str(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl MnemonicHash
|
||||
{
|
||||
/// Create mnemonic hash from this string slice
|
||||
#[inline] pub fn from_str(string: impl AsRef<str>) -> Self
|
||||
{
|
||||
Self::from_slice(string.as_ref().as_bytes())
|
||||
}
|
||||
/// Create mnemonic hash from this slice
|
||||
pub fn from_slice(data: impl AsRef<[u8]>) -> Self
|
||||
{
|
||||
Self(sha256::compute_slice_iter(iter![data.as_ref(), defaults::MNEMONIC_SALT.as_ref()]), OnceCell::new())
|
||||
}
|
||||
|
||||
/// The inner hash
|
||||
#[inline] pub fn as_hash(&self) -> &Sha256Hash
|
||||
{
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Create a mnemonic from this hash
|
||||
///
|
||||
/// # Notes
|
||||
/// This does not salt the hash, the hasher is responsible for salting the hash with `defaults::MNEMONIC_SALT`.
|
||||
#[inline] pub fn from_hash(hash: Sha256Hash) -> Self
|
||||
{
|
||||
Self(hash, OnceCell::new())
|
||||
}
|
||||
|
||||
fn render(&self) -> String
|
||||
{
|
||||
#[allow(unused_mut)] let mut end;
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(feature="short-mnemonics")] {
|
||||
let last = &self.0.as_ref()[..(sha256::SIZE/2)];
|
||||
let first = &self.0.as_ref()[(sha256::SIZE/2)..];
|
||||
end = [0u8; sha256::SIZE/2];
|
||||
for (e, (f, l)) in end.iter_mut().zip(first.iter().copied().zip(last.iter().copied()))
|
||||
{
|
||||
*e = f ^ l;
|
||||
}
|
||||
} else {
|
||||
end = &self.0.as_ref()[..];
|
||||
}
|
||||
}
|
||||
base64::encode(end).chars().filter_map(|x| {
|
||||
Some(match x {
|
||||
'/' => 'ł',
|
||||
'+' => 'þ',
|
||||
'=' => return None,
|
||||
x => x
|
||||
})
|
||||
}).collect()
|
||||
}
|
||||
|
||||
#[inline] fn render_cache(&self) -> &String
|
||||
{
|
||||
self.1.get_or_init(|| {
|
||||
self.render()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MnemonicHash> for Sha256Hash
|
||||
{
|
||||
fn from(from: MnemonicHash) -> Self
|
||||
{
|
||||
from.0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl AsRef<Sha256Hash> for MnemonicHash
|
||||
{
|
||||
fn as_ref(&self) -> &Sha256Hash
|
||||
{
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<Sha256Hash> for MnemonicHash
|
||||
{
|
||||
fn borrow(&self) -> &Sha256Hash {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for MnemonicHash
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
||||
{
|
||||
write!(f, "{}", self.render_cache())
|
||||
}
|
||||
}
|
||||
|
||||
/// What kind of salt to use for hashing `MnemonicHash`.
|
||||
///
|
||||
/// This is intended to be global state.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum MnemonicSaltKind
|
||||
{
|
||||
None,
|
||||
Specific([u8; defaults::MNEMONIC_SALT_SIZE]),
|
||||
Random,
|
||||
}
|
||||
|
||||
impl MnemonicSaltKind
|
||||
{
|
||||
/// Get as a slice.
|
||||
pub fn as_slice(&self) -> Option<&[u8]>
|
||||
{
|
||||
lazy_static! {
|
||||
static ref RANDOM_SALT: [u8; defaults::MNEMONIC_SALT_SIZE] = {
|
||||
let mut output = [0u8; defaults::MNEMONIC_SALT_SIZE];
|
||||
getrandom::getrandom(&mut output[..]).expect("rng fatal");
|
||||
output
|
||||
};
|
||||
}
|
||||
Some(match self {
|
||||
Self::None => return None,
|
||||
Self::Random => &RANDOM_SALT[..],
|
||||
Self::Specific(u) => &u[..],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for MnemonicSaltKind
|
||||
{
|
||||
#[inline] fn as_ref(&self) -> &[u8]
|
||||
{
|
||||
self.as_slice().unwrap_or(&[])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests
|
||||
{
|
||||
#[test]
|
||||
fn mnemonics()
|
||||
{
|
||||
let _mnemonic = super::MnemonicHash::from_slice(b"hello world");
|
||||
}
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
use super::*;
|
||||
use khash::ctx::Context;
|
||||
//use khash::salt::Salt;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
/// A newtype tripcode string
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||
pub struct Tripcode(String);
|
||||
|
||||
lazy_static!{
|
||||
static ref CONTEXT: Context = Context::new(defaults::TRIPCODE_ALGO, defaults::TRIPCODE_SALT);
|
||||
}
|
||||
|
||||
impl Tripcode
|
||||
{
|
||||
/// Generate a tripcode from this string.
|
||||
pub fn generate(from: impl AsRef<[u8]>) -> Result<Self, khash::error::Error>
|
||||
{
|
||||
khash::generate(&CONTEXT, from).map(Self)
|
||||
}
|
||||
/// Create a tripcode that *is* this string
|
||||
#[inline] pub fn special(string: String) -> Self
|
||||
{
|
||||
Self(string)
|
||||
}
|
||||
/// As a string
|
||||
#[inline] pub fn as_str(&self) -> &str
|
||||
{
|
||||
&self.0
|
||||
}
|
||||
/// Consume into regular string
|
||||
#[inline] pub fn into_inner(self) -> String
|
||||
{
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Tripcode> for String
|
||||
{
|
||||
#[inline] fn from(from: Tripcode) -> Self
|
||||
{
|
||||
from.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Tripcode
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
||||
{
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for Tripcode
|
||||
{
|
||||
fn as_ref(&self) -> &str
|
||||
{
|
||||
&self.0[..]
|
||||
}
|
||||
}
|
||||
|
||||
impl std::borrow::Borrow<String> for Tripcode
|
||||
{
|
||||
fn borrow(&self) -> &String
|
||||
{
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in new issue