1use super::{Crypto, Uuid, Version, H160};
18use serde::{
19 de::{DeserializeOwned, Error, MapAccess, Visitor},
20 Deserialize, Deserializer, Serialize, Serializer,
21};
22use serde_json;
23use std::{
24 fmt,
25 io::{Read, Write},
26};
27
28#[derive(Debug, PartialEq)]
30pub struct OpaqueKeyFile {
31 key_file: KeyFile,
32}
33
34impl Serialize for OpaqueKeyFile {
35 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
36 where S: Serializer {
37 self.key_file.serialize(serializer)
38 }
39}
40
41impl<T> From<T> for OpaqueKeyFile
42where T: Into<KeyFile>
43{
44 fn from(val: T) -> Self {
45 OpaqueKeyFile {
46 key_file: val.into(),
47 }
48 }
49}
50
51#[derive(Debug, PartialEq, Serialize)]
52pub struct KeyFile {
53 pub id: Uuid,
54 pub version: Version,
55 pub crypto: Crypto,
56 pub address: Option<H160>,
57 pub name: Option<String>,
58 pub meta: Option<String>,
59}
60
61enum KeyFileField {
62 Id,
63 Version,
64 Crypto,
65 Address,
66 Name,
67 Meta,
68}
69
70impl<'a> Deserialize<'a> for KeyFileField {
71 fn deserialize<D>(deserializer: D) -> Result<KeyFileField, D::Error>
72 where D: Deserializer<'a> {
73 deserializer.deserialize_any(KeyFileFieldVisitor)
74 }
75}
76
77struct KeyFileFieldVisitor;
78
79impl<'a> Visitor<'a> for KeyFileFieldVisitor {
80 type Value = KeyFileField;
81
82 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
83 write!(formatter, "a valid key file field")
84 }
85
86 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
87 where E: Error {
88 match value {
89 "id" => Ok(KeyFileField::Id),
90 "version" => Ok(KeyFileField::Version),
91 "crypto" => Ok(KeyFileField::Crypto),
92 "Crypto" => Ok(KeyFileField::Crypto),
93 "address" => Ok(KeyFileField::Address),
94 "name" => Ok(KeyFileField::Name),
95 "meta" => Ok(KeyFileField::Meta),
96 _ => Err(Error::custom(format!("Unknown field: '{}'", value))),
97 }
98 }
99}
100
101impl<'a> Deserialize<'a> for KeyFile {
102 fn deserialize<D>(deserializer: D) -> Result<KeyFile, D::Error>
103 where D: Deserializer<'a> {
104 static FIELDS: &[&str] =
105 &["id", "version", "crypto", "Crypto", "address"];
106 deserializer.deserialize_struct("KeyFile", FIELDS, KeyFileVisitor)
107 }
108}
109
110fn none_if_empty<'a, T>(v: Option<serde_json::Value>) -> Option<T>
111where T: DeserializeOwned {
112 v.and_then(|v| {
113 if v.is_null() {
114 None
115 } else {
116 serde_json::from_value(v).ok()
117 }
118 })
119}
120
121struct KeyFileVisitor;
122impl<'a> Visitor<'a> for KeyFileVisitor {
123 type Value = KeyFile;
124
125 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
126 write!(formatter, "a valid key object")
127 }
128
129 fn visit_map<V>(self, mut visitor: V) -> Result<Self::Value, V::Error>
130 where V: MapAccess<'a> {
131 let mut id = None;
132 let mut version = None;
133 let mut crypto = None;
134 let mut address = None;
135 let mut name = None;
136 let mut meta = None;
137
138 loop {
139 match visitor.next_key()? {
140 Some(KeyFileField::Id) => {
141 id = Some(visitor.next_value()?);
142 }
143 Some(KeyFileField::Version) => {
144 version = Some(visitor.next_value()?);
145 }
146 Some(KeyFileField::Crypto) => {
147 crypto = Some(visitor.next_value()?);
148 }
149 Some(KeyFileField::Address) => {
150 address = Some(visitor.next_value()?);
151 }
152 Some(KeyFileField::Name) => {
153 name = none_if_empty(visitor.next_value().ok())
154 }
155 Some(KeyFileField::Meta) => {
156 meta = none_if_empty(visitor.next_value().ok())
157 }
158 None => {
159 break;
160 }
161 }
162 }
163
164 let id = match id {
165 Some(id) => id,
166 None => return Err(V::Error::missing_field("id")),
167 };
168
169 let version = match version {
170 Some(version) => version,
171 None => return Err(V::Error::missing_field("version")),
172 };
173
174 let crypto = match crypto {
175 Some(crypto) => crypto,
176 None => return Err(V::Error::missing_field("crypto")),
177 };
178
179 let result = KeyFile {
180 id,
181 version,
182 crypto,
183 address,
184 name,
185 meta,
186 };
187
188 Ok(result)
189 }
190}
191
192impl KeyFile {
193 pub fn load<R>(reader: R) -> Result<Self, serde_json::Error>
194 where R: Read {
195 serde_json::from_reader(reader)
196 }
197
198 pub fn write<W>(&self, writer: &mut W) -> Result<(), serde_json::Error>
199 where W: Write {
200 serde_json::to_writer(writer, self)
201 }
202}
203
204#[cfg(test)]
205mod tests {
206 use crate::json::{
207 Aes128Ctr, Cipher, Crypto, Kdf, KeyFile, Scrypt, Uuid, Version,
208 };
209 use serde_json;
210 use std::str::FromStr;
211
212 #[test]
213 fn basic_keyfile() {
214 let json = r#"
215 {
216 "address": "6edddfc6349aff20bc6467ccf276c5b52487f7a8",
217 "crypto": {
218 "cipher": "aes-128-ctr",
219 "ciphertext": "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc",
220 "cipherparams": {
221 "iv": "b5a7ec855ec9e2c405371356855fec83"
222 },
223 "kdf": "scrypt",
224 "kdfparams": {
225 "dklen": 32,
226 "n": 262144,
227 "p": 1,
228 "r": 8,
229 "salt": "1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209"
230 },
231 "mac": "46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f"
232 },
233 "id": "8777d9f6-7860-4b9b-88b7-0b57ee6b3a73",
234 "version": 3,
235 "name": "Test",
236 "meta": "{}"
237 }"#;
238
239 let expected = KeyFile {
240 id: Uuid::from_str("8777d9f6-7860-4b9b-88b7-0b57ee6b3a73").unwrap(),
241 version: Version::V3,
242 address: Some("6edddfc6349aff20bc6467ccf276c5b52487f7a8".into()),
243 crypto: Crypto {
244 cipher: Cipher::Aes128Ctr(Aes128Ctr {
245 iv: "b5a7ec855ec9e2c405371356855fec83".into(),
246 }),
247 ciphertext: "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc".into(),
248 kdf: Kdf::Scrypt(Scrypt {
249 n: 262_144,
250 dklen: 32,
251 p: 1,
252 r: 8,
253 salt: "1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209".into(),
254 }),
255 mac: "46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f".into(),
256 },
257 name: Some("Test".to_owned()),
258 meta: Some("{}".to_owned()),
259 };
260
261 let keyfile: KeyFile = serde_json::from_str(json).unwrap();
262 assert_eq!(keyfile, expected);
263 }
264
265 #[test]
266 fn capital_crypto_keyfile() {
267 let json = r#"
268 {
269 "address": "6edddfc6349aff20bc6467ccf276c5b52487f7a8",
270 "Crypto": {
271 "cipher": "aes-128-ctr",
272 "ciphertext": "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc",
273 "cipherparams": {
274 "iv": "b5a7ec855ec9e2c405371356855fec83"
275 },
276 "kdf": "scrypt",
277 "kdfparams": {
278 "dklen": 32,
279 "n": 262144,
280 "p": 1,
281 "r": 8,
282 "salt": "1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209"
283 },
284 "mac": "46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f"
285 },
286 "id": "8777d9f6-7860-4b9b-88b7-0b57ee6b3a73",
287 "version": 3
288 }"#;
289
290 let expected = KeyFile {
291 id: "8777d9f6-7860-4b9b-88b7-0b57ee6b3a73".into(),
292 version: Version::V3,
293 address: Some("6edddfc6349aff20bc6467ccf276c5b52487f7a8".into()),
294 crypto: Crypto {
295 cipher: Cipher::Aes128Ctr(Aes128Ctr {
296 iv: "b5a7ec855ec9e2c405371356855fec83".into(),
297 }),
298 ciphertext: "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc".into(),
299 kdf: Kdf::Scrypt(Scrypt {
300 n: 262_144,
301 dklen: 32,
302 p: 1,
303 r: 8,
304 salt: "1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209".into(),
305 }),
306 mac: "46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f".into(),
307 },
308 name: None,
309 meta: None,
310 };
311
312 let keyfile: KeyFile = serde_json::from_str(json).unwrap();
313 assert_eq!(keyfile, expected);
314 }
315
316 #[test]
317 fn to_and_from_json() {
318 let file = KeyFile {
319 id: "8777d9f6-7860-4b9b-88b7-0b57ee6b3a73".into(),
320 version: Version::V3,
321 address: Some("6edddfc6349aff20bc6467ccf276c5b52487f7a8".into()),
322 crypto: Crypto {
323 cipher: Cipher::Aes128Ctr(Aes128Ctr {
324 iv: "b5a7ec855ec9e2c405371356855fec83".into(),
325 }),
326 ciphertext: "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc".into(),
327 kdf: Kdf::Scrypt(Scrypt {
328 n: 262_144,
329 dklen: 32,
330 p: 1,
331 r: 8,
332 salt: "1e8642fdf1f87172492c1412fc62f8db75d796cdfa9c53c3f2b11e44a2a1b209".into(),
333 }),
334 mac: "46325c5d4e8c991ad2683d525c7854da387138b6ca45068985aa4959fa2b8c8f".into(),
335 },
336 name: Some("Test".to_owned()),
337 meta: None,
338 };
339
340 let serialized = serde_json::to_string(&file).unwrap();
341 println!("{}", serialized);
342 let deserialized = serde_json::from_str(&serialized).unwrap();
343
344 assert_eq!(file, deserialized);
345 }
346}