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