eth_keystore/
keystore.rs

1use hex::{FromHex, ToHex};
2use serde::{de::Deserializer, ser::Serializer, Deserialize, Serialize};
3use uuid::Uuid;
4
5#[cfg(feature = "geth-compat")]
6use ethereum_types::H160 as Address;
7
8#[derive(Debug, Deserialize, Serialize)]
9/// This struct represents the deserialized form of an encrypted JSON keystore based on the
10/// [Web3 Secret Storage Definition](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition).
11pub struct EthKeystore {
12    #[cfg(feature = "geth-compat")]
13    pub address: Address,
14
15    pub crypto: CryptoJson,
16    pub id: Uuid,
17    pub version: u8,
18}
19
20#[derive(Debug, Deserialize, Serialize)]
21/// Represents the "crypto" part of an encrypted JSON keystore.
22pub struct CryptoJson {
23    pub cipher: String,
24    pub cipherparams: CipherparamsJson,
25    #[serde(serialize_with = "buffer_to_hex", deserialize_with = "hex_to_buffer")]
26    pub ciphertext: Vec<u8>,
27    pub kdf: KdfType,
28    pub kdfparams: KdfparamsType,
29    #[serde(serialize_with = "buffer_to_hex", deserialize_with = "hex_to_buffer")]
30    pub mac: Vec<u8>,
31}
32
33#[derive(Debug, Deserialize, Serialize)]
34/// Represents the "cipherparams" part of an encrypted JSON keystore.
35pub struct CipherparamsJson {
36    #[serde(serialize_with = "buffer_to_hex", deserialize_with = "hex_to_buffer")]
37    pub iv: Vec<u8>,
38}
39
40#[derive(Debug, Deserialize, Eq, PartialEq, Serialize)]
41#[serde(rename_all = "lowercase")]
42/// Types of key derivition functions supported by the Web3 Secret Storage.
43pub enum KdfType {
44    Pbkdf2,
45    Scrypt,
46}
47
48#[derive(Debug, Deserialize, Eq, PartialEq, Serialize)]
49#[serde(untagged)]
50/// Defines the various parameters used in the supported KDFs.
51pub enum KdfparamsType {
52    Pbkdf2 {
53        c: u32,
54        dklen: u8,
55        prf: String,
56        #[serde(serialize_with = "buffer_to_hex", deserialize_with = "hex_to_buffer")]
57        salt: Vec<u8>,
58    },
59    Scrypt {
60        dklen: u8,
61        n: u32,
62        p: u32,
63        r: u32,
64        #[serde(serialize_with = "buffer_to_hex", deserialize_with = "hex_to_buffer")]
65        salt: Vec<u8>,
66    },
67}
68
69fn buffer_to_hex<T, S>(buffer: &T, serializer: S) -> Result<S::Ok, S::Error>
70where
71    T: AsRef<[u8]>,
72    S: Serializer,
73{
74    serializer.serialize_str(&buffer.encode_hex::<String>())
75}
76
77fn hex_to_buffer<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
78where
79    D: Deserializer<'de>,
80{
81    use serde::de::Error;
82    String::deserialize(deserializer)
83        .and_then(|string| Vec::from_hex(&string).map_err(|err| Error::custom(err.to_string())))
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89
90    #[cfg(feature = "geth-compat")]
91    #[test]
92    fn deserialize_geth_compat_keystore() {
93        let data = r#"
94        {
95            "address": "00000398232e2064f896018496b4b44b3d62751f",
96            "crypto": {
97                "cipher": "aes-128-ctr",
98                "ciphertext": "4f784cd629a7caf34b488e36fb96aad8a8f943a6ce31c7deab950c5e3a5b1c43",
99                "cipherparams": {
100                    "iv": "76f07196b3c94f25b8f34d869493f640"
101                },
102                "kdf": "scrypt",
103                "kdfparams": {
104                    "dklen": 32,
105                    "n": 262144,
106                    "p": 1,
107                    "r": 8,
108                    "salt": "1e7be4ce8351dd1710b0885438414b1748a81f1af510eda11e4d1f99c8d43975"
109                },
110                "mac": "5b5433575a2418c1c813337a88b4099baa2f534e5dabeba86979d538c1f594d8"
111            },
112            "id": "6c4485f3-3cc0-4081-848e-8bf489f2c262",
113            "version": 3
114        }"#;
115        let keystore: EthKeystore = serde_json::from_str(data).unwrap();
116        assert_eq!(
117            keystore.address.as_bytes().to_vec(),
118            hex::decode("00000398232e2064f896018496b4b44b3d62751f").unwrap()
119        );
120    }
121
122    #[cfg(not(feature = "geth-compat"))]
123    #[test]
124    fn test_deserialize_pbkdf2() {
125        let data = r#"
126        {
127            "crypto" : {
128                "cipher" : "aes-128-ctr",
129                "cipherparams" : {
130                    "iv" : "6087dab2f9fdbbfaddc31a909735c1e6"
131                },
132                "ciphertext" : "5318b4d5bcd28de64ee5559e671353e16f075ecae9f99c7a79a38af5f869aa46",
133                "kdf" : "pbkdf2",
134                "kdfparams" : {
135                    "c" : 262144,
136                    "dklen" : 32,
137                    "prf" : "hmac-sha256",
138                    "salt" : "ae3cd4e7013836a3df6bd7241b12db061dbe2c6785853cce422d148a624ce0bd"
139                },
140                "mac" : "517ead924a9d0dc3124507e3393d175ce3ff7c1e96529c6c555ce9e51205e9b2"
141            },
142            "id" : "3198bc9c-6672-5ab3-d995-4942343ae5b6",
143            "version" : 3
144        }"#;
145        let keystore: EthKeystore = serde_json::from_str(data).unwrap();
146        assert_eq!(keystore.version, 3);
147        assert_eq!(
148            keystore.id,
149            Uuid::parse_str("3198bc9c-6672-5ab3-d995-4942343ae5b6").unwrap()
150        );
151        assert_eq!(keystore.crypto.cipher, "aes-128-ctr");
152        assert_eq!(
153            keystore.crypto.cipherparams.iv,
154            Vec::from_hex("6087dab2f9fdbbfaddc31a909735c1e6").unwrap()
155        );
156        assert_eq!(
157            keystore.crypto.ciphertext,
158            Vec::from_hex("5318b4d5bcd28de64ee5559e671353e16f075ecae9f99c7a79a38af5f869aa46")
159                .unwrap()
160        );
161        assert_eq!(keystore.crypto.kdf, KdfType::Pbkdf2);
162        assert_eq!(
163            keystore.crypto.kdfparams,
164            KdfparamsType::Pbkdf2 {
165                c: 262144,
166                dklen: 32,
167                prf: String::from("hmac-sha256"),
168                salt: Vec::from_hex(
169                    "ae3cd4e7013836a3df6bd7241b12db061dbe2c6785853cce422d148a624ce0bd"
170                )
171                .unwrap(),
172            }
173        );
174        assert_eq!(
175            keystore.crypto.mac,
176            Vec::from_hex("517ead924a9d0dc3124507e3393d175ce3ff7c1e96529c6c555ce9e51205e9b2")
177                .unwrap()
178        );
179    }
180
181    #[cfg(not(feature = "geth-compat"))]
182    #[test]
183    fn test_deserialize_scrypt() {
184        let data = r#"
185        {
186            "crypto" : {
187                "cipher" : "aes-128-ctr",
188                "cipherparams" : {
189                    "iv" : "83dbcc02d8ccb40e466191a123791e0e"
190                },
191                "ciphertext" : "d172bf743a674da9cdad04534d56926ef8358534d458fffccd4e6ad2fbde479c",
192                "kdf" : "scrypt",
193                "kdfparams" : {
194                    "dklen" : 32,
195                    "n" : 262144,
196                    "p" : 8,
197                    "r" : 1,
198                    "salt" : "ab0c7876052600dd703518d6fc3fe8984592145b591fc8fb5c6d43190334ba19"
199                },
200                "mac" : "2103ac29920d71da29f15d75b4a16dbe95cfd7ff8faea1056c33131d846e3097"
201            },
202            "id" : "3198bc9c-6672-5ab3-d995-4942343ae5b6",
203            "version" : 3
204        }"#;
205        let keystore: EthKeystore = serde_json::from_str(data).unwrap();
206        assert_eq!(keystore.version, 3);
207        assert_eq!(
208            keystore.id,
209            Uuid::parse_str("3198bc9c-6672-5ab3-d995-4942343ae5b6").unwrap()
210        );
211        assert_eq!(keystore.crypto.cipher, "aes-128-ctr");
212        assert_eq!(
213            keystore.crypto.cipherparams.iv,
214            Vec::from_hex("83dbcc02d8ccb40e466191a123791e0e").unwrap()
215        );
216        assert_eq!(
217            keystore.crypto.ciphertext,
218            Vec::from_hex("d172bf743a674da9cdad04534d56926ef8358534d458fffccd4e6ad2fbde479c")
219                .unwrap()
220        );
221        assert_eq!(keystore.crypto.kdf, KdfType::Scrypt);
222        assert_eq!(
223            keystore.crypto.kdfparams,
224            KdfparamsType::Scrypt {
225                dklen: 32,
226                n: 262144,
227                p: 8,
228                r: 1,
229                salt: Vec::from_hex(
230                    "ab0c7876052600dd703518d6fc3fe8984592145b591fc8fb5c6d43190334ba19"
231                )
232                .unwrap(),
233            }
234        );
235        assert_eq!(
236            keystore.crypto.mac,
237            Vec::from_hex("2103ac29920d71da29f15d75b4a16dbe95cfd7ff8faea1056c33131d846e3097")
238                .unwrap()
239        );
240    }
241}