cfxstore/json/
vault_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::de::Error;
19use serde_derive::{Deserialize, Serialize};
20use serde_json::{self, error, value::Value};
21use std::io::{Read, Write};
22
23/// Meta key name for vault field
24const VAULT_NAME_META_KEY: &str = "vault";
25
26/// Key file as stored in vaults
27#[derive(Debug, PartialEq, Serialize, Deserialize)]
28pub struct VaultKeyFile {
29    /// Key id
30    pub id: Uuid,
31    /// Key version
32    pub version: Version,
33    /// Secret, encrypted with account password
34    pub crypto: Crypto,
35    /// Serialized `VaultKeyMeta`, encrypted with vault password
36    pub metacrypto: Crypto,
37}
38
39/// Data, stored in `VaultKeyFile::metacrypto`
40#[derive(Debug, PartialEq, Serialize, Deserialize)]
41pub struct VaultKeyMeta {
42    /// Key address
43    pub address: H160,
44    /// Key name
45    pub name: Option<String>,
46    /// Key metadata
47    pub meta: Option<String>,
48}
49
50/// Insert vault name to the JSON meta field
51pub fn insert_vault_name_to_json_meta(
52    meta: &str, vault_name: &str,
53) -> Result<String, error::Error> {
54    let mut meta = if meta.is_empty() {
55        Value::Object(serde_json::Map::new())
56    } else {
57        serde_json::from_str(meta)?
58    };
59
60    if let Some(meta_obj) = meta.as_object_mut() {
61        meta_obj.insert(
62            VAULT_NAME_META_KEY.to_owned(),
63            Value::String(vault_name.to_owned()),
64        );
65        serde_json::to_string(meta_obj)
66    } else {
67        Err(error::Error::custom(
68            "Meta is expected to be a serialized JSON object",
69        ))
70    }
71}
72
73/// Remove vault name from the JSON meta field
74pub fn remove_vault_name_from_json_meta(
75    meta: &str,
76) -> Result<String, error::Error> {
77    let mut meta = if meta.is_empty() {
78        Value::Object(serde_json::Map::new())
79    } else {
80        serde_json::from_str(meta)?
81    };
82
83    if let Some(meta_obj) = meta.as_object_mut() {
84        meta_obj.remove(VAULT_NAME_META_KEY);
85        serde_json::to_string(meta_obj)
86    } else {
87        Err(error::Error::custom(
88            "Meta is expected to be a serialized JSON object",
89        ))
90    }
91}
92
93impl VaultKeyFile {
94    pub fn load<R>(reader: R) -> Result<Self, serde_json::Error>
95    where R: Read {
96        serde_json::from_reader(reader)
97    }
98
99    pub fn write<W>(&self, writer: &mut W) -> Result<(), serde_json::Error>
100    where W: Write {
101        serde_json::to_writer(writer, self)
102    }
103}
104
105impl VaultKeyMeta {
106    pub fn load(bytes: &[u8]) -> Result<Self, serde_json::Error> {
107        serde_json::from_slice(&bytes)
108    }
109
110    pub fn write(&self) -> Result<Vec<u8>, serde_json::Error> {
111        let s = serde_json::to_string(self)?;
112        Ok(s.as_bytes().into())
113    }
114}
115
116#[cfg(test)]
117mod test {
118    use crate::json::{
119        insert_vault_name_to_json_meta, remove_vault_name_from_json_meta,
120        Aes128Ctr, Cipher, Crypto, Kdf, Pbkdf2, Prf, VaultKeyFile, Version,
121    };
122    use serde_json;
123
124    #[test]
125    fn to_and_from_json() {
126        let file = VaultKeyFile {
127			id: "08d82c39-88e3-7a71-6abb-89c8f36c3ceb".into(),
128			version: Version::V3,
129			crypto: Crypto {
130				cipher: Cipher::Aes128Ctr(Aes128Ctr {
131					iv: "fecb968bbc8c7e608a89ebcfe53a41d0".into(),
132				}),
133				ciphertext: "4befe0a66d9a4b6fec8e39eb5c90ac5dafdeaab005fff1af665fd1f9af925c91".into(),
134				kdf: Kdf::Pbkdf2(Pbkdf2 {
135					c: 10240,
136					dklen: 32,
137					prf: Prf::HmacSha256,
138					salt: "f17731e84ecac390546692dbd4ccf6a3a2720dc9652984978381e61c28a471b2".into(),
139				}),
140				mac: "7c7c3daafb24cf11eb3079dfb9064a11e92f309a0ee1dd676486bab119e686b7".into(),
141			},
142			metacrypto: Crypto {
143				cipher: Cipher::Aes128Ctr(Aes128Ctr {
144					iv: "9c353fb3f894fc05946843616c26bb3f".into(),
145				}),
146				ciphertext: "fef0d113d7576c1702daf380ad6f4c5408389e57991cae2a174facd74bd549338e1014850bddbab7eb486ff5f5c9c5532800c6a6d4db2be2212cd5cd3769244ab230e1f369e8382a9e6d7c0a".into(),
147				kdf: Kdf::Pbkdf2(Pbkdf2 {
148					c: 10240,
149					dklen: 32,
150					prf: Prf::HmacSha256,
151					salt: "aca82865174a82249a198814b263f43a631f272cbf7ed329d0f0839d259c652a".into(),
152				}),
153				mac: "b7413946bfe459d2801268dc331c04b3a84d92be11ef4dd9a507f895e8d9b5bd".into(),
154			}
155		};
156
157        let serialized = serde_json::to_string(&file).unwrap();
158        let deserialized = serde_json::from_str(&serialized).unwrap();
159
160        assert_eq!(file, deserialized);
161    }
162
163    #[test]
164    fn vault_name_inserted_to_json_meta() {
165        assert_eq!(
166            insert_vault_name_to_json_meta(r#""#, "MyVault").unwrap(),
167            r#"{"vault":"MyVault"}"#
168        );
169        assert_eq!(
170            insert_vault_name_to_json_meta(
171                r#"{"tags":["kalabala"]}"#,
172                "MyVault"
173            )
174            .unwrap(),
175            r#"{"tags":["kalabala"],"vault":"MyVault"}"#
176        );
177    }
178
179    #[test]
180    fn vault_name_not_inserted_to_json_meta() {
181        assert!(
182            insert_vault_name_to_json_meta(r#"///3533"#, "MyVault").is_err()
183        );
184        assert!(
185            insert_vault_name_to_json_meta(r#""string""#, "MyVault").is_err()
186        );
187    }
188
189    #[test]
190    fn vault_name_removed_from_json_meta() {
191        assert_eq!(
192            remove_vault_name_from_json_meta(r#"{"vault":"MyVault"}"#).unwrap(),
193            r#"{}"#
194        );
195        assert_eq!(
196            remove_vault_name_from_json_meta(
197                r#"{"tags":["kalabala"],"vault":"MyVault"}"#
198            )
199            .unwrap(),
200            r#"{"tags":["kalabala"]}"#
201        );
202    }
203
204    #[test]
205    fn vault_name_not_removed_from_json_meta() {
206        assert!(remove_vault_name_from_json_meta(r#"///3533"#).is_err());
207        assert!(remove_vault_name_from_json_meta(r#""string""#).is_err());
208    }
209}