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
80impl Serialize for Hash {
81    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
82    where
83        S: serde::Serializer,
84    {
85        serializer.serialize_str(&self.dump_base36())
86    }
87}
88
89impl<'de> Deserialize<'de> for Hash {
90    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
91    where
92        D: serde::Deserializer<'de>,
93    {
94        let s = String::deserialize(deserializer)?;
95        Self::new_from_base36(&s).ok_or_else(|| serde::de::Error::custom("Invalid base36 hash"))
96    }
97}
98
99impl Deref for Hash {
100    type Target = [u8; 32];
101    fn deref(&self) -> &Self::Target {
102        &self.0
103    }
104}
105
106impl fmt::Debug for Hash {
107    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
108        write!(f, "Hash: {}", self.dump_base36())
109    }
110}
111
112/// Store sign and verify ed25519
113#[derive(Clone, PartialEq, Eq, Encode, Decode, Copy, Hash)]
114pub struct Signature([u8; 64]);
115
116impl Signature {
117    /// Create a signature by signing some data
118    pub fn new_signature(private: &mut Private, data: &[u8]) -> Self {
119        let mut key = SigningKey::from_bytes(private.dump_buf());
120        let signature = key.sign(data);
121        Signature(signature.to_bytes()) // [u8; 64]
122    }
123
124    /// Create a signature with the raw signature bytes (no signing)
125    pub fn new_from_buf(signature: &[u8; 64]) -> Self {
126        Signature(signature.clone())
127    }
128
129    /// Validate a signature and return true if valid
130    pub fn validate_with_public(&self, public: &Public, data: &[u8]) -> Result<bool, Error> {
131        let key = VerifyingKey::from_bytes(public.dump_buf())?;
132        Ok(key
133            .verify_strict(data, &DalekSignature::from_bytes(&self.0))
134            .is_ok())
135    }
136
137    /// Validate a signature and return true if valid
138    pub fn validate_with_private(&self, private: &Private, data: &[u8]) -> Result<bool, Error> {
139        let key = SigningKey::from_bytes(private.dump_buf());
140        Ok(key
141            .verify_strict(data, &DalekSignature::from_bytes(&self.0))
142            .is_ok())
143    }
144
145    /// Create a signature from a base 36 string
146    pub fn new_from_base36(s: &str) -> Option<Self> {
147        // Convert base36 string to bytes
148        let big_int = BigUint::parse_bytes(s.as_bytes(), 36)?;
149        let mut buf = big_int.to_bytes_be();
150
151        // Ensure the buffer is exactly 64 bytes
152        if buf.len() > 64 {
153            return None;
154        } else if buf.len() < 64 {
155            // Pad with zeros at the front
156            let mut padded = vec![0u8; 64 - buf.len()];
157            padded.extend(buf);
158            buf = padded;
159        }
160
161        // Convert Vec<u8> to [u8; 64]
162        let buf: [u8; 64] = buf.try_into().ok()?;
163
164        Some(Self(buf))
165    }
166
167    pub fn dump_base36(&self) -> String {
168        let big_int = BigUint::from_bytes_be(&self.0);
169        big_int.to_str_radix(36)
170    }
171}
172
173impl Serialize for Signature {
174    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
175    where
176        S: serde::Serializer,
177    {
178        serializer.serialize_str(&self.dump_base36())
179    }
180}
181
182impl<'de> Deserialize<'de> for Signature {
183    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
184    where
185        D: serde::Deserializer<'de>,
186    {
187        let s = String::deserialize(deserializer)?;
188        Self::new_from_base36(&s).ok_or_else(|| serde::de::Error::custom("Invalid base36 signature"))
189    }
190}
191
192impl Deref for Signature {
193    type Target = [u8; 64]; // match the array size
194    fn deref(&self) -> &Self::Target {
195        &self.0
196    }
197}
198
199impl fmt::Debug for Signature {
200    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
201        write!(f, "Signature: {}", self.dump_base36())
202    }
203}