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 serde_json;
23use std::{
24    fmt,
25    io::{Read, Write},
26};
27
28/// Public opaque type representing serializable `KeyFile`.
29#[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}