cfxstore/json/
key_file.rs

1// Copyright 2015-2019 Parity Technologies (UK) Ltd.
2// This file is part of Parity Ethereum.
3
4// Parity Ethereum is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Parity Ethereum is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Parity Ethereum.  If not, see <http://www.gnu.org/licenses/>.
16
17use 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/// Public opaque type representing serializable `KeyFile`.
28#[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}