darkfi_sdk/crypto/
keypair.rs

1/* This file is part of DarkFi (https://dark.fi)
2 *
3 * Copyright (C) 2020-2025 Dyne.org foundation
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as
7 * published by the Free Software Foundation, either version 3 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18
19use core::str::FromStr;
20
21#[cfg(feature = "async")]
22use darkfi_serial::async_trait;
23use darkfi_serial::{SerialDecodable, SerialEncodable};
24use halo2_gadgets::ecc::chip::FixedPoint;
25use pasta_curves::{
26    arithmetic::CurveAffine,
27    group::{
28        ff::{Field, PrimeField},
29        Curve, Group, GroupEncoding,
30    },
31    pallas,
32};
33use rand_core::{CryptoRng, RngCore};
34
35use super::{constants::NullifierK, util::fp_mod_fv};
36use crate::error::ContractError;
37
38/// Keypair structure holding a `SecretKey` and its respective `PublicKey`
39#[derive(Copy, Clone, PartialEq, Eq, Debug, SerialEncodable, SerialDecodable)]
40pub struct Keypair {
41    pub secret: SecretKey,
42    pub public: PublicKey,
43}
44
45impl Keypair {
46    /// Instantiate a new `Keypair` given a `SecretKey`
47    pub fn new(secret: SecretKey) -> Self {
48        Self { secret, public: PublicKey::from_secret(secret) }
49    }
50
51    /// Generate a new `Keypair` object given a source of randomness
52    pub fn random(rng: &mut (impl CryptoRng + RngCore)) -> Self {
53        Self::new(SecretKey::random(rng))
54    }
55}
56
57impl Default for Keypair {
58    /// Default Keypair used in genesis block generation
59    fn default() -> Self {
60        let secret = SecretKey::from(pallas::Base::from(42));
61        let public = PublicKey::from_secret(secret);
62        Self { secret, public }
63    }
64}
65
66/// Structure holding a secret key, wrapping a `pallas::Base` element.
67#[derive(Copy, Clone, PartialEq, Eq, Debug, SerialEncodable, SerialDecodable)]
68pub struct SecretKey(pallas::Base);
69
70impl SecretKey {
71    /// Get the inner object wrapped by `SecretKey`
72    pub fn inner(&self) -> pallas::Base {
73        self.0
74    }
75
76    /// Generate a new `SecretKey` given a source of randomness
77    pub fn random(rng: &mut (impl CryptoRng + RngCore)) -> Self {
78        Self(pallas::Base::random(rng))
79    }
80
81    /// Instantiate a `SecretKey` given 32 bytes. Returns an error
82    /// if the representation is noncanonical.
83    pub fn from_bytes(bytes: [u8; 32]) -> Result<Self, ContractError> {
84        match pallas::Base::from_repr(bytes).into() {
85            Some(k) => Ok(Self(k)),
86            None => Err(ContractError::IoError("Could not convert bytes to SecretKey".to_string())),
87        }
88    }
89}
90
91impl From<pallas::Base> for SecretKey {
92    fn from(x: pallas::Base) -> Self {
93        Self(x)
94    }
95}
96
97impl FromStr for SecretKey {
98    type Err = ContractError;
99
100    /// Tries to create a `SecretKey` object from a base58 encoded string.
101    fn from_str(enc: &str) -> Result<Self, Self::Err> {
102        let decoded = bs58::decode(enc).into_vec()?;
103        if decoded.len() != 32 {
104            return Err(Self::Err::IoError(
105                "Failed decoding SecretKey from bytes, len is not 32".to_string(),
106            ))
107        }
108
109        Self::from_bytes(decoded.try_into().unwrap())
110    }
111}
112
113impl core::fmt::Display for SecretKey {
114    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
115        let disp: String = bs58::encode(self.0.to_repr()).into_string();
116        write!(f, "{}", disp)
117    }
118}
119
120/// Structure holding a public key, wrapping a `pallas::Point` element.
121#[derive(Copy, Clone, PartialEq, Eq, Debug, SerialEncodable, SerialDecodable)]
122pub struct PublicKey(pallas::Point);
123
124impl PublicKey {
125    /// Get the inner object wrapped by `PublicKey`
126    pub fn inner(&self) -> pallas::Point {
127        self.0
128    }
129
130    /// Derive a new `PublicKey` object given a `SecretKey`
131    pub fn from_secret(s: SecretKey) -> Self {
132        let p = NullifierK.generator() * fp_mod_fv(s.inner());
133        Self(p)
134    }
135
136    /// Instantiate a `PublicKey` given 32 bytes. Returns an error
137    /// if the representation is noncanonical.
138    pub fn from_bytes(bytes: [u8; 32]) -> Result<Self, ContractError> {
139        match <subtle::CtOption<pallas::Point> as Into<Option<pallas::Point>>>::into(
140            pallas::Point::from_bytes(&bytes),
141        ) {
142            Some(k) => {
143                if bool::from(k.is_identity()) {
144                    return Err(ContractError::IoError(
145                        "Could not convert bytes to PublicKey".to_string(),
146                    ))
147                }
148
149                Ok(Self(k))
150            }
151            None => Err(ContractError::IoError("Could not convert bytes to PublicKey".to_string())),
152        }
153    }
154
155    /// Downcast the `PublicKey` to 32 bytes of `pallas::Point`
156    pub fn to_bytes(&self) -> [u8; 32] {
157        self.0.to_bytes()
158    }
159
160    /// Fetch the `x` coordinate of this `PublicKey`
161    pub fn x(&self) -> pallas::Base {
162        *self.0.to_affine().coordinates().unwrap().x()
163    }
164
165    /// Fetch the `y` coordinate of this `PublicKey`
166    pub fn y(&self) -> pallas::Base {
167        *self.0.to_affine().coordinates().unwrap().y()
168    }
169
170    /// Fetch the `x` and `y` coordinates of this `PublicKey` as a tuple
171    pub fn xy(&self) -> (pallas::Base, pallas::Base) {
172        let coords = self.0.to_affine().coordinates().unwrap();
173        (*coords.x(), *coords.y())
174    }
175}
176
177impl TryFrom<pallas::Point> for PublicKey {
178    type Error = ContractError;
179
180    fn try_from(x: pallas::Point) -> Result<Self, Self::Error> {
181        if bool::from(x.is_identity()) {
182            return Err(ContractError::IoError(
183                "Could not convert identity point to PublicKey".to_string(),
184            ))
185        }
186
187        Ok(Self(x))
188    }
189}
190
191impl FromStr for PublicKey {
192    type Err = ContractError;
193
194    /// Tries to create a `PublicKey` object from a base58 encoded string.
195    fn from_str(enc: &str) -> Result<Self, Self::Err> {
196        let decoded = bs58::decode(enc).into_vec()?;
197        if decoded.len() != 32 {
198            return Err(Self::Err::IoError(
199                "Failed decoding PublicKey from bytes, len is not 32".to_string(),
200            ))
201        }
202
203        Self::from_bytes(decoded.try_into().unwrap())
204    }
205}
206
207impl core::fmt::Display for PublicKey {
208    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
209        let disp: String = bs58::encode(self.0.to_bytes()).into_string();
210        write!(f, "{}", disp)
211    }
212}