emerald_vault/convert/proto/
pk.rs

1use crate::{
2    blockchain::EthereumAddress,
3    convert::error::ConversionError,
4    proto::{
5        common::FileType as proto_FileType,
6        crypto::Encrypted as proto_Encrypted,
7        pk::{
8            EthereumPK3 as proto_EthereumPK3,
9            EthereumPrivateKey as proto_EthereumPrivateKey,
10            PrivateKey as proto_PrivateKey,
11        },
12    },
13    structs::{
14        crypto::Encrypted,
15        pk::{EthereumPk3, PrivateKeyHolder, PrivateKeyType},
16    },
17};
18use chrono::{TimeZone, Utc};
19use protobuf::Message;
20use std::{convert::TryFrom, str::FromStr};
21use uuid::Uuid;
22
23/// Read from Protobuf bytes
24impl TryFrom<&[u8]> for PrivateKeyHolder {
25    type Error = ConversionError;
26    fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
27        let m = proto_PrivateKey::parse_from_bytes(data)?;
28        if m.has_ethereum() {
29            let pk = m.get_ethereum();
30            if pk.has_pk() {
31                let pk = pk.get_pk();
32                let key = match &pk.value.clone().into_option() {
33                    Some(v) => Encrypted::try_from(v),
34                    None => Err(ConversionError::FieldIsEmpty("encrypted".to_string())),
35                }?;
36                let address = match EthereumAddress::from_str(pk.get_address()) {
37                    Ok(a) => Some(a),
38                    Err(_) => None,
39                };
40                let result = EthereumPk3 { address, key };
41                let pk = PrivateKeyType::EthereumPk(result);
42                let created_at = Utc
43                    .timestamp_millis_opt(m.get_created_at() as i64)
44                    .single()
45                    .unwrap_or_else(|| Utc.timestamp_millis_opt(0).unwrap());
46                let result = PrivateKeyHolder {
47                    id: Uuid::from_slice(m.get_id())
48                        .map_err(|_| ConversionError::InvalidFieldValue("id".to_string()))?,
49                    pk,
50                    created_at,
51                };
52                Ok(result)
53            } else {
54                Err(ConversionError::FieldIsEmpty("pk".to_string()))
55            }
56        } else {
57            Err(ConversionError::InvalidFieldValue("pk".to_string()))
58        }
59    }
60}
61
62/// Read from Protobuf bytes
63impl TryFrom<Vec<u8>> for PrivateKeyHolder {
64    type Error = ConversionError;
65
66    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
67        PrivateKeyHolder::try_from(value.as_slice())
68    }
69}
70
71/// Write as Protobuf bytes
72impl TryFrom<PrivateKeyHolder> for Vec<u8> {
73    type Error = ConversionError;
74
75    fn try_from(value: PrivateKeyHolder) -> Result<Self, Self::Error> {
76        let mut result = proto_PrivateKey::default();
77        result.set_file_type(proto_FileType::FILE_PK);
78        result.set_id(value.id.as_bytes().to_vec());
79        let mut ethereum = proto_EthereumPrivateKey::default();
80        match &value.pk {
81            PrivateKeyType::EthereumPk(it) => {
82                let mut ethereum_pk3 = proto_EthereumPK3::default();
83                if let Some(address) = it.address { ethereum_pk3.set_address(address.to_string()) };
84                ethereum_pk3.set_value(proto_Encrypted::try_from(&it.key)?);
85                ethereum.set_pk(ethereum_pk3);
86            }
87        }
88        result.set_ethereum(ethereum);
89        result.set_created_at(value.created_at.timestamp_millis() as u64);
90        result
91            .write_to_bytes()
92            .map_err(ConversionError::from)
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    use crate::{proto::pk::PrivateKey as proto_PrivateKey, structs::pk::PrivateKeyHolder};
99    use chrono::{TimeZone, Utc};
100    use protobuf::{Message, ProtobufEnum};
101    use std::{
102        convert::{TryFrom, TryInto},
103        str::FromStr,
104    };
105    use uuid::Uuid;
106
107    #[test]
108    fn write_as_protobuf() {
109        let mut pk = PrivateKeyHolder::generate_ethereum_raw("test").unwrap();
110        pk.id = Uuid::from_str("18ba0447-81f3-40d7-bab1-e74de07a1001").unwrap();
111        pk.created_at = Utc.timestamp_millis_opt(1592624592679).unwrap();
112
113        let b: Vec<u8> = pk.try_into().unwrap();
114        assert!(!b.is_empty());
115        let act = proto_PrivateKey::parse_from_bytes(b.as_slice()).unwrap();
116        assert_eq!(act.get_file_type().value(), 2);
117        assert_eq!(
118            Uuid::from_slice(act.get_id()).unwrap(),
119            Uuid::from_str("18ba0447-81f3-40d7-bab1-e74de07a1001").unwrap()
120        );
121        assert!(act.has_ethereum());
122        assert_eq!(act.created_at, 1592624592679);
123    }
124
125    #[test]
126    fn write_and_read() {
127        let mut pk = PrivateKeyHolder::generate_ethereum_raw("test").unwrap();
128        pk.id = Uuid::from_str("18ba0447-81f3-40d7-bab1-e74de07a1001").unwrap();
129        pk.created_at = Utc.timestamp_millis_opt(1592624592679).unwrap();
130
131        let b: Vec<u8> = pk.try_into().unwrap();
132        assert!(!b.is_empty());
133        let act = PrivateKeyHolder::try_from(b).unwrap();
134        assert_eq!(act.id.to_string(), "18ba0447-81f3-40d7-bab1-e74de07a1001");
135        assert!(act.decrypt("test".as_bytes(), None).is_ok());
136        assert_eq!(act.created_at, Utc.timestamp_millis_opt(1592624592679).unwrap());
137    }
138
139    #[test]
140    fn ignore_big_created_at() {
141        let pk = PrivateKeyHolder::generate_ethereum_raw("test").unwrap();
142        let tmp: Vec<u8> = pk.try_into().unwrap();
143        let mut m = proto_PrivateKey::parse_from_bytes(tmp.as_slice()).unwrap();
144        m.set_created_at((i64::MAX as u64) + 100);
145
146        let buf = m.write_to_bytes().unwrap();
147        let act = PrivateKeyHolder::try_from(buf).unwrap();
148        assert_eq!(act.created_at.timestamp_millis(), 0);
149    }
150}