web3_keystore/
keystore.rs

1use serde::{Deserialize, Serialize};
2use uuid::Uuid;
3
4#[derive(Debug, Deserialize, Serialize)]
5/// Opaque type that represents an encrypted keystore based on the
6/// [Web3 Secret Storage Definition](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition).
7pub struct KeyStore {
8    /// Version of the Web3 Secret storage definition specification.
9    pub version: u8,
10    /// Unique identifier for the keystore.
11    pub id: Uuid,
12    /// Optional public address for the keystore (non-standard).
13    pub address: Option<String>,
14    /// Optional label for the keystore (non-standard).
15    pub label: Option<String>,
16    /// Crypto part of the keystore.
17    pub(crate) crypto: CryptoData,
18}
19
20#[derive(Debug, Deserialize, Serialize)]
21/// Represents the "crypto" part of an encrypted keystore.
22pub struct CryptoData {
23    pub cipher: String,
24    pub cipherparams: CipherParams,
25    #[serde(
26        serialize_with = "hex::serde::serialize",
27        deserialize_with = "hex::serde::deserialize"
28    )]
29    pub ciphertext: Vec<u8>,
30    pub kdf: KdfType,
31    pub kdfparams: KdfParamsType,
32    #[serde(
33        serialize_with = "hex::serde::serialize",
34        deserialize_with = "hex::serde::deserialize"
35    )]
36    pub mac: Vec<u8>,
37}
38
39#[derive(Debug, Deserialize, Serialize)]
40/// Represents the "cipherparams" part of an encrypted JSON keystore.
41pub struct CipherParams {
42    #[serde(
43        serialize_with = "hex::serde::serialize",
44        deserialize_with = "hex::serde::deserialize"
45    )]
46    pub iv: Vec<u8>,
47}
48
49#[derive(Debug, Deserialize, PartialEq, Serialize)]
50#[serde(rename_all = "lowercase")]
51/// Types of key derivation functions supported by Web3 Secret Storage.
52pub enum KdfType {
53    Pbkdf2,
54    Scrypt,
55}
56
57#[derive(Debug, Deserialize, PartialEq, Serialize)]
58#[serde(untagged)]
59/// Defines the various parameters used in the supported KDFs.
60pub enum KdfParamsType {
61    Pbkdf2 {
62        c: u32,
63        dklen: u8,
64        prf: String,
65        #[serde(
66            serialize_with = "hex::serde::serialize",
67            deserialize_with = "hex::serde::deserialize"
68        )]
69        salt: Vec<u8>,
70    },
71    Scrypt {
72        dklen: u8,
73        n: u32,
74        p: u32,
75        r: u32,
76        #[serde(
77            serialize_with = "hex::serde::serialize",
78            deserialize_with = "hex::serde::deserialize"
79        )]
80        salt: Vec<u8>,
81    },
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87    use anyhow::Result;
88
89    #[test]
90    fn test_deserialize_pbkdf2() -> Result<()> {
91        let data = r#"
92        {
93            "crypto" : {
94                "cipher" : "aes-128-ctr",
95                "cipherparams" : {
96                    "iv" : "6087dab2f9fdbbfaddc31a909735c1e6"
97                },
98                "ciphertext" : "5318b4d5bcd28de64ee5559e671353e16f075ecae9f99c7a79a38af5f869aa46",
99                "kdf" : "pbkdf2",
100                "kdfparams" : {
101                    "c" : 262144,
102                    "dklen" : 32,
103                    "prf" : "hmac-sha256",
104                    "salt" : "ae3cd4e7013836a3df6bd7241b12db061dbe2c6785853cce422d148a624ce0bd"
105                },
106                "mac" : "517ead924a9d0dc3124507e3393d175ce3ff7c1e96529c6c555ce9e51205e9b2"
107            },
108            "id" : "3198bc9c-6672-5ab3-d995-4942343ae5b6",
109            "version" : 3
110        }"#;
111        let keystore: KeyStore = serde_json::from_str(data)?;
112        assert_eq!(keystore.version, 3);
113        assert_eq!(
114            keystore.id,
115            Uuid::parse_str("3198bc9c-6672-5ab3-d995-4942343ae5b6")?
116        );
117        assert_eq!(keystore.crypto.cipher, "aes-128-ctr");
118        assert_eq!(
119            keystore.crypto.cipherparams.iv,
120            hex::decode("6087dab2f9fdbbfaddc31a909735c1e6")?
121        );
122        assert_eq!(
123            keystore.crypto.ciphertext,
124            hex::decode("5318b4d5bcd28de64ee5559e671353e16f075ecae9f99c7a79a38af5f869aa46")?
125        );
126        assert_eq!(keystore.crypto.kdf, KdfType::Pbkdf2);
127        assert_eq!(
128            keystore.crypto.kdfparams,
129            KdfParamsType::Pbkdf2 {
130                c: 262144,
131                dklen: 32,
132                prf: String::from("hmac-sha256"),
133                salt: hex::decode(
134                    "ae3cd4e7013836a3df6bd7241b12db061dbe2c6785853cce422d148a624ce0bd"
135                )?,
136            }
137        );
138        assert_eq!(
139            keystore.crypto.mac,
140            hex::decode("517ead924a9d0dc3124507e3393d175ce3ff7c1e96529c6c555ce9e51205e9b2")?
141        );
142        Ok(())
143    }
144
145    #[test]
146    fn test_deserialize_scrypt() -> Result<()> {
147        let data = r#"
148        {
149            "crypto" : {
150                "cipher" : "aes-128-ctr",
151                "cipherparams" : {
152                    "iv" : "83dbcc02d8ccb40e466191a123791e0e"
153                },
154                "ciphertext" : "d172bf743a674da9cdad04534d56926ef8358534d458fffccd4e6ad2fbde479c",
155                "kdf" : "scrypt",
156                "kdfparams" : {
157                    "dklen" : 32,
158                    "n" : 262144,
159                    "p" : 8,
160                    "r" : 1,
161                    "salt" : "ab0c7876052600dd703518d6fc3fe8984592145b591fc8fb5c6d43190334ba19"
162                },
163                "mac" : "2103ac29920d71da29f15d75b4a16dbe95cfd7ff8faea1056c33131d846e3097"
164            },
165            "id" : "3198bc9c-6672-5ab3-d995-4942343ae5b6",
166            "version" : 3
167        }"#;
168        let keystore: KeyStore = serde_json::from_str(data)?;
169        assert_eq!(keystore.version, 3);
170        assert_eq!(
171            keystore.id,
172            Uuid::parse_str("3198bc9c-6672-5ab3-d995-4942343ae5b6")?
173        );
174        assert_eq!(keystore.crypto.cipher, "aes-128-ctr");
175        assert_eq!(
176            keystore.crypto.cipherparams.iv,
177            hex::decode("83dbcc02d8ccb40e466191a123791e0e")?
178        );
179        assert_eq!(
180            keystore.crypto.ciphertext,
181            hex::decode("d172bf743a674da9cdad04534d56926ef8358534d458fffccd4e6ad2fbde479c")?
182        );
183        assert_eq!(keystore.crypto.kdf, KdfType::Scrypt);
184        assert_eq!(
185            keystore.crypto.kdfparams,
186            KdfParamsType::Scrypt {
187                dklen: 32,
188                n: 262144,
189                p: 8,
190                r: 1,
191                salt: hex::decode(
192                    "ab0c7876052600dd703518d6fc3fe8984592145b591fc8fb5c6d43190334ba19"
193                )?,
194            }
195        );
196        assert_eq!(
197            keystore.crypto.mac,
198            hex::decode("2103ac29920d71da29f15d75b4a16dbe95cfd7ff8faea1056c33131d846e3097")?
199        );
200        Ok(())
201    }
202}