hoprd_keypair/
keystore.rs

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