use crate::{
blockchain::EthereumAddress,
convert::error::ConversionError,
proto::{
common::FileType as proto_FileType,
crypto::Encrypted as proto_Encrypted,
pk::{
EthereumPK3 as proto_EthereumPK3,
EthereumPrivateKey as proto_EthereumPrivateKey,
PrivateKey as proto_PrivateKey,
},
},
structs::{
crypto::Encrypted,
pk::{EthereumPk3, PrivateKeyHolder, PrivateKeyType},
},
};
use chrono::{TimeZone, Utc};
use protobuf::Message;
use std::{convert::TryFrom, str::FromStr};
use uuid::Uuid;
impl TryFrom<&[u8]> for PrivateKeyHolder {
type Error = ConversionError;
fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
let m = proto_PrivateKey::parse_from_bytes(data)?;
if m.has_ethereum() {
let pk = m.get_ethereum();
if pk.has_pk() {
let pk = pk.get_pk();
let key = match &pk.value.clone().into_option() {
Some(v) => Encrypted::try_from(v),
None => Err(ConversionError::FieldIsEmpty("encrypted".to_string())),
}?;
let address = match EthereumAddress::from_str(pk.get_address()) {
Ok(a) => Some(a),
Err(_) => None,
};
let result = EthereumPk3 { address, key };
let pk = PrivateKeyType::EthereumPk(result);
let created_at = Utc
.timestamp_millis_opt(m.get_created_at() as i64)
.single()
.unwrap_or_else(|| Utc.timestamp_millis_opt(0).unwrap());
let result = PrivateKeyHolder {
id: Uuid::from_slice(m.get_id())
.map_err(|_| ConversionError::InvalidFieldValue("id".to_string()))?,
pk,
created_at,
};
Ok(result)
} else {
Err(ConversionError::FieldIsEmpty("pk".to_string()))
}
} else {
Err(ConversionError::InvalidFieldValue("pk".to_string()))
}
}
}
impl TryFrom<Vec<u8>> for PrivateKeyHolder {
type Error = ConversionError;
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
PrivateKeyHolder::try_from(value.as_slice())
}
}
impl TryFrom<PrivateKeyHolder> for Vec<u8> {
type Error = ConversionError;
fn try_from(value: PrivateKeyHolder) -> Result<Self, Self::Error> {
let mut result = proto_PrivateKey::default();
result.set_file_type(proto_FileType::FILE_PK);
result.set_id(value.id.as_bytes().to_vec());
let mut ethereum = proto_EthereumPrivateKey::default();
match &value.pk {
PrivateKeyType::EthereumPk(it) => {
let mut ethereum_pk3 = proto_EthereumPK3::default();
if let Some(address) = it.address { ethereum_pk3.set_address(address.to_string()) };
ethereum_pk3.set_value(proto_Encrypted::try_from(&it.key)?);
ethereum.set_pk(ethereum_pk3);
}
}
result.set_ethereum(ethereum);
result.set_created_at(value.created_at.timestamp_millis() as u64);
result
.write_to_bytes()
.map_err(ConversionError::from)
}
}
#[cfg(test)]
mod tests {
use crate::{proto::pk::PrivateKey as proto_PrivateKey, structs::pk::PrivateKeyHolder};
use chrono::{TimeZone, Utc};
use protobuf::{Message, ProtobufEnum};
use std::{
convert::{TryFrom, TryInto},
str::FromStr,
};
use uuid::Uuid;
#[test]
fn write_as_protobuf() {
let mut pk = PrivateKeyHolder::generate_ethereum_raw("test").unwrap();
pk.id = Uuid::from_str("18ba0447-81f3-40d7-bab1-e74de07a1001").unwrap();
pk.created_at = Utc.timestamp_millis_opt(1592624592679).unwrap();
let b: Vec<u8> = pk.try_into().unwrap();
assert!(!b.is_empty());
let act = proto_PrivateKey::parse_from_bytes(b.as_slice()).unwrap();
assert_eq!(act.get_file_type().value(), 2);
assert_eq!(
Uuid::from_slice(act.get_id()).unwrap(),
Uuid::from_str("18ba0447-81f3-40d7-bab1-e74de07a1001").unwrap()
);
assert!(act.has_ethereum());
assert_eq!(act.created_at, 1592624592679);
}
#[test]
fn write_and_read() {
let mut pk = PrivateKeyHolder::generate_ethereum_raw("test").unwrap();
pk.id = Uuid::from_str("18ba0447-81f3-40d7-bab1-e74de07a1001").unwrap();
pk.created_at = Utc.timestamp_millis_opt(1592624592679).unwrap();
let b: Vec<u8> = pk.try_into().unwrap();
assert!(!b.is_empty());
let act = PrivateKeyHolder::try_from(b).unwrap();
assert_eq!(act.id.to_string(), "18ba0447-81f3-40d7-bab1-e74de07a1001");
assert!(act.decrypt("test".as_bytes(), None).is_ok());
assert_eq!(act.created_at, Utc.timestamp_millis_opt(1592624592679).unwrap());
}
#[test]
fn ignore_big_created_at() {
let pk = PrivateKeyHolder::generate_ethereum_raw("test").unwrap();
let tmp: Vec<u8> = pk.try_into().unwrap();
let mut m = proto_PrivateKey::parse_from_bytes(tmp.as_slice()).unwrap();
m.set_created_at((i64::MAX as u64) + 100);
let buf = m.write_to_bytes().unwrap();
let act = PrivateKeyHolder::try_from(buf).unwrap();
assert_eq!(act.created_at.timestamp_millis(), 0);
}
}