1use crate::{Keypair, KeypairInfo, Scrypt, ss58};
20use anyhow::{Result, anyhow};
21use base64::{Engine as _, engine::general_purpose::STANDARD};
22use rand::RngCore;
23use serde::{Deserialize, Serialize};
25use std::time::{SystemTime, UNIX_EPOCH};
26
27#[derive(Clone, Debug, Serialize, Deserialize, Default)]
29pub struct Keystore {
30 pub encoded: String,
32 #[serde(default)]
34 pub encoding: Encoding,
35 pub address: String,
37 #[serde(default)]
39 pub meta: Meta,
40}
41
42impl Keystore {
43 const NONCE_LENGTH: usize = 24;
45
46 pub fn encrypt(keypair: Keypair, passphrase: Option<&[u8]>) -> Result<Self> {
48 let info = KeypairInfo::from(keypair);
49 if let Some(passphrase) = passphrase {
50 Self::encrypt_scrypt(info, passphrase)
51 } else {
52 Self::encrypt_none(info)
53 }
54 }
55
56 pub fn encrypt_scrypt(info: KeypairInfo, passphrase: &[u8]) -> Result<Self> {
58 let mut encoded = Vec::new();
59
60 let scrypt = Scrypt::default();
62 let passwd = scrypt.passwd(passphrase)?;
63 encoded.extend_from_slice(&scrypt.encode());
64
65 let mut nonce = [0; Self::NONCE_LENGTH];
67 rand::thread_rng().fill_bytes(&mut nonce);
68 encoded.extend_from_slice(&nonce);
69
70 let encrypted = nacl::secret_box::pack(&info.encode(), &nonce, &passwd[..32])
72 .map_err(|e| anyhow!("{e:?}"))?;
73 encoded.extend_from_slice(&encrypted);
74
75 Ok(Self {
76 encoded: STANDARD.encode(&encoded),
77 address: ss58::encode(&info.public)?,
78 encoding: Encoding::scrypt(),
79 ..Default::default()
80 })
81 }
82
83 pub fn encrypt_none(info: KeypairInfo) -> Result<Self> {
85 Ok(Self {
86 encoded: STANDARD.encode(info.encode()),
87 address: ss58::encode(&info.public)?,
88 ..Default::default()
89 })
90 }
91
92 pub fn decrypt(&self, passphrase: Option<&[u8]>) -> Result<Keypair> {
94 if let Some(passphrase) = passphrase {
95 if !self.encoding.is_scrypt() {
96 return Err(anyhow!(
97 "unsupported key deriven function {}.",
98 self.encoding.ty[0]
99 ));
100 }
101
102 self.decrypt_scrypt(passphrase)
103 } else {
104 if self.encoding.is_xsalsa20_poly1305() {
105 return Err(anyhow!("password required to decode encrypted data."));
106 }
107
108 self.decrypt_none()
109 }
110 }
111
112 pub fn decrypt_scrypt(&self, passphrase: &[u8]) -> Result<Keypair> {
114 let decoded = self.decoded()?;
115
116 let mut encoded_scrypt = [0; Scrypt::ENCODED_LENGTH];
118 encoded_scrypt.copy_from_slice(&decoded[..Scrypt::ENCODED_LENGTH]);
119 let passwd = Scrypt::decode(encoded_scrypt).passwd(passphrase)?;
120
121 let encrypted = &decoded[Scrypt::ENCODED_LENGTH..];
123 let secret = nacl::secret_box::open(
124 &encrypted[Self::NONCE_LENGTH..],
125 &encrypted[..Self::NONCE_LENGTH],
126 &passwd[..32],
127 )
128 .map_err(|e| anyhow!("{e:?}"))?;
129
130 KeypairInfo::decode(&secret[..KeypairInfo::ENCODED_LENGTH])?.into_keypair()
132 }
133
134 pub fn decrypt_none(&self) -> Result<Keypair> {
136 KeypairInfo::decode(&self.decoded()?)?.into_keypair()
137 }
138
139 pub fn with_name(mut self, name: &str) -> Self {
141 self.meta.name = name.to_owned();
142 self
143 }
144
145 fn decoded(&self) -> Result<Vec<u8>> {
147 STANDARD.decode(&self.encoded).map_err(Into::into)
148 }
149}
150
151#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
153pub struct Encoding {
154 pub content: (String, String),
159
160 #[serde(rename = "type")]
166 pub ty: Vec<String>,
167
168 pub version: String,
170}
171
172impl Encoding {
173 pub fn none() -> Self {
175 Self {
176 content: ("pkcs8".into(), "sr25519".into()),
177 ty: vec!["none".into()],
178 version: "3".to_string(),
179 }
180 }
181
182 pub fn scrypt() -> Self {
184 Self {
185 content: ("pkcs8".into(), "sr25519".into()),
186 ty: vec!["scrypt".into(), "xsalsa20-poly1305".into()],
187 ..Default::default()
188 }
189 }
190
191 pub fn is_scrypt(&self) -> bool {
193 self.ty.first() == Some(&"scrypt".into())
194 }
195
196 pub fn is_xsalsa20_poly1305(&self) -> bool {
198 self.ty.get(1) == Some(&"xsalsa20-poly1305".into())
199 }
200}
201
202impl Default for Encoding {
203 fn default() -> Self {
204 Self::none()
205 }
206}
207
208#[derive(Clone, Debug, Serialize, Deserialize)]
210pub struct Meta {
211 pub name: String,
213
214 #[serde(rename = "whenCreated")]
216 pub when_created: u128,
217}
218
219impl Default for Meta {
220 fn default() -> Self {
221 Self {
222 name: "".into(),
223 when_created: SystemTime::now()
224 .duration_since(UNIX_EPOCH)
225 .expect("time went backwards")
226 .as_millis(),
227 }
228 }
229}