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