snap_coin/crypto/
mod.rs

1use argon2::{Argon2, Params};
2use bincode::{Decode, Encode};
3use ed25519_dalek::SigningKey;
4use ed25519_dalek::ed25519::Error;
5use ed25519_dalek::ed25519::signature::SignerMut;
6use ed25519_dalek::{Signature as DalekSignature, VerifyingKey};
7use num_bigint::BigUint;
8use serde::{Deserialize, Serialize};
9use std::fmt;
10use std::ops::Deref;
11
12use keys::{Private, Public};
13
14/// Public / Private key logic
15pub mod keys;
16
17pub const MAGIC_BYTES: [u8; 10] = [205, 198, 59, 175, 94, 82, 224, 9, 114, 173];
18
19pub fn argon2_hash(input: &[u8]) -> [u8; 32] {
20    // WARNING: SLOW
21    let params = Params::new(8 * 1024, 1, 2, Some(32)).unwrap();
22    let argon2 = Argon2::new(argon2::Algorithm::Argon2id, argon2::Version::V0x13, params);
23    let mut hash = [0u8; 32];
24    argon2
25        .hash_password_into(input, &MAGIC_BYTES, &mut hash)
26        .unwrap();
27    hash
28}
29
30/// Store and hash Argon2 hashes (compare too)
31/// When used in in hashmaps, the already hashed argon 2 digest gets re-hashed, for speed
32#[derive(Clone, Copy, PartialEq, Eq, Encode, Decode, std::hash::Hash)]
33pub struct Hash([u8; 32]);
34
35impl Hash {
36    /// Create a new hash by hashing some data
37    pub fn new(data: &[u8]) -> Self {
38        Hash(argon2_hash(data))
39    }
40
41    /// Create a new hash with a buffer of an already existing hash
42    pub const fn new_from_buf(hash_buf: [u8; 32]) -> Self {
43        Hash(hash_buf)
44    }
45
46    /// Compare this hash with some data (check if the data hash is the same)
47    pub fn compare_with_data(&self, other_data: &[u8]) -> bool {
48        let computed = argon2_hash(other_data);
49        computed == self.0
50    }
51
52    /// Create a new hash from a already existing hash encoded in a base36 string
53    pub fn new_from_base36(s: &str) -> Option<Self> {
54        // Convert base36 string to bytes
55        let big_int = BigUint::parse_bytes(s.as_bytes(), 36)?;
56        let mut buf = big_int.to_bytes_be();
57
58        // Ensure the buffer is exactly 32 bytes
59        if buf.len() > 32 {
60            return None;
61        } else if buf.len() < 32 {
62            // Pad with zeros at the front
63            let mut padded = vec![0u8; 32 - buf.len()];
64            padded.extend(buf);
65            buf = padded;
66        }
67
68        // Convert Vec<u8> to [u8; 32]
69        let buf: [u8; 32] = buf.try_into().ok()?;
70
71        Some(Hash(buf))
72    }
73
74    pub fn dump_base36(&self) -> String {
75        let big_int = BigUint::from_bytes_be(&self.0);
76        big_int.to_str_radix(36)
77    }
78
79    pub fn dump_buf(&self) -> [u8; 32] {
80        self.0
81    }
82}
83
84impl Serialize for Hash {
85    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
86    where
87        S: serde::Serializer,
88    {
89        serializer.serialize_str(&self.dump_base36())
90    }
91}
92
93impl<'de> Deserialize<'de> for Hash {
94    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
95    where
96        D: serde::Deserializer<'de>,
97    {
98        let s = String::deserialize(deserializer)?;
99        Self::new_from_base36(&s).ok_or_else(|| serde::de::Error::custom("Invalid base36 hash"))
100    }
101}
102
103impl Deref for Hash {
104    type Target = [u8; 32];
105    fn deref(&self) -> &Self::Target {
106        &self.0
107    }
108}
109
110impl fmt::Debug for Hash {
111    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
112        write!(f, "Hash: {}", self.dump_base36())
113    }
114}
115
116/// Store sign and verify ed25519
117#[derive(Clone, PartialEq, Eq, Encode, Decode, Copy, Hash)]
118pub struct Signature([u8; 64]);
119
120impl Signature {
121    /// Create a signature by signing some data
122    pub fn new_signature(private: &mut Private, data: &[u8]) -> Self {
123        let mut key = SigningKey::from_bytes(private.dump_buf());
124        let signature = key.sign(data);
125        Signature(signature.to_bytes()) // [u8; 64]
126    }
127
128    /// Create a signature with the raw signature bytes (no signing)
129    pub fn new_from_buf(signature: &[u8; 64]) -> Self {
130        Signature(signature.clone())
131    }
132
133    /// Validate a signature and return true if valid
134    pub fn validate_with_public(&self, public: &Public, data: &[u8]) -> Result<bool, Error> {
135        let key = VerifyingKey::from_bytes(public.dump_buf())?;
136        Ok(key
137            .verify_strict(data, &DalekSignature::from_bytes(&self.0))
138            .is_ok())
139    }
140
141    /// Validate a signature and return true if valid
142    pub fn validate_with_private(&self, private: &Private, data: &[u8]) -> Result<bool, Error> {
143        let key = SigningKey::from_bytes(private.dump_buf());
144        Ok(key
145            .verify_strict(data, &DalekSignature::from_bytes(&self.0))
146            .is_ok())
147    }
148
149    /// Create a signature from a base 36 string
150    pub fn new_from_base36(s: &str) -> Option<Self> {
151        // Convert base36 string to bytes
152        let big_int = BigUint::parse_bytes(s.as_bytes(), 36)?;
153        let mut buf = big_int.to_bytes_be();
154
155        // Ensure the buffer is exactly 64 bytes
156        if buf.len() > 64 {
157            return None;
158        } else if buf.len() < 64 {
159            // Pad with zeros at the front
160            let mut padded = vec![0u8; 64 - buf.len()];
161            padded.extend(buf);
162            buf = padded;
163        }
164
165        // Convert Vec<u8> to [u8; 64]
166        let buf: [u8; 64] = buf.try_into().ok()?;
167
168        Some(Self(buf))
169    }
170
171    pub fn dump_base36(&self) -> String {
172        let big_int = BigUint::from_bytes_be(&self.0);
173        big_int.to_str_radix(36)
174    }
175
176    pub fn dump_buf(&self) -> [u8; 64] {
177        self.0
178    }
179}
180
181impl Serialize for Signature {
182    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
183    where
184        S: serde::Serializer,
185    {
186        serializer.serialize_str(&self.dump_base36())
187    }
188}
189
190impl<'de> Deserialize<'de> for Signature {
191    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
192    where
193        D: serde::Deserializer<'de>,
194    {
195        let s = String::deserialize(deserializer)?;
196        Self::new_from_base36(&s).ok_or_else(|| serde::de::Error::custom("Invalid base36 signature"))
197    }
198}
199
200impl Deref for Signature {
201    type Target = [u8; 64]; // match the array size
202    fn deref(&self) -> &Self::Target {
203        &self.0
204    }
205}
206
207impl fmt::Debug for Signature {
208    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
209        write!(f, "Signature: {}", self.dump_base36())
210    }
211}