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