copernica_packets/
response_data.rs

1use {
2    crate::{
3        Data, PublicIdentity, PublicIdentityInterface, PrivateIdentityInterface, Tag, Nonce
4    },
5    copernica_common::constants::*,
6    std::fmt,
7    anyhow::{anyhow, Result},
8    rand::Rng,
9    cryptoxide::{chacha20poly1305::{ChaCha20Poly1305}},
10    log::{error},
11};
12#[derive(Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
13pub enum ResponseData {
14    ClearText {
15        data: Box<Data>,
16    },
17    CypherText {
18        data: Box<Data>,
19        tag: Tag,
20    },
21}
22impl fmt::Display for ResponseData {
23    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
24        match &*self {
25            ResponseData::ClearText { data } => write!(f, "RD::ClearText: {}", data),
26            ResponseData::CypherText { data, tag } => write!(f, "RD::CypherText: {:?} Tag: {:?}", data, tag),
27        }
28    }
29}
30impl fmt::Debug for ResponseData {
31    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
32        match &*self {
33            ResponseData::ClearText { data } => write!(f, "RD::ClearText: {:?}", data),
34            ResponseData::CypherText { data, tag } => write!(f, "RD::CypherText: {:?} Tag: {:?}", data, tag),
35        }
36    }
37}
38impl ResponseData {
39    pub fn as_bytes(&self) -> Vec<u8> {
40        let mut buf: Vec<u8> = vec![];
41        match self {
42            ResponseData::ClearText { data } => {
43                buf.extend_from_slice(&data.as_bytes());
44                buf
45            },
46            ResponseData::CypherText { data, tag } => {
47                buf.extend_from_slice(&tag.as_bytes());
48                buf.extend_from_slice(&data.as_bytes());
49                buf
50            },
51        }
52    }
53    pub fn from_bytes(data: &[u8]) -> Result<Self> {
54        match data.len() {
55            CYPHERTEXT_RESPONSE_DATA_SIZE => {
56                let mut tag = Tag([0u8; TAG_SIZE]);
57                tag.0.clone_from_slice(&data[..TAG_SIZE]);
58                let data = Data::new(&data[TAG_SIZE..])?;
59                Ok(ResponseData::CypherText { tag, data: Box::new(data) })
60            },
61            CLEARTEXT_RESPONSE_DATA_SIZE => {
62                let data = Data::new(&data[..])?;
63                Ok(ResponseData::ClearText { data: Box::new(data) })
64            }
65            _ => {
66                Err(anyhow!("Length of data used to reconstruct a ResponseData is unrecognised"))
67            }
68        }
69    }
70    pub fn insert(response_sid: PrivateIdentityInterface, request_pid: PublicIdentityInterface, data: Vec<u8>, nonce: Nonce) -> Result<Self> {
71        match request_pid {
72            PublicIdentityInterface::Present { public_identity } => {
73                if data.len() > DATA_SIZE {
74                    return Err(anyhow!("Ensure data.len() passed into ResponseData::cypher_text() is not greater than {}", DATA_SIZE))
75                }
76                let mut rng = rand::thread_rng();
77                let length = data.len();
78                let padding: Vec<u8> = (0..(DATA_SIZE - length)).map(|_| rng.gen()).collect();
79                //let padding: Vec<u8> = (0..(DATA_SIZE - length)).map(|_| 0).collect();
80                let b1 = length as u8;
81                let b2 = (length >> 8) as u8;
82                let u8_nonce: u8 = rng.gen();
83                let metadata = vec![u8_nonce, b2, b1];
84                let data = vec![data, padding, metadata];
85                let flattened = data.into_iter().flatten().collect::<Vec<u8>>();
86                let mut data: [u8; FRAGMENT_SIZE] = [0; FRAGMENT_SIZE];
87                data.copy_from_slice(&flattened[0..FRAGMENT_SIZE]);
88                let mut nonce_reverse = nonce.clone();
89                nonce_reverse.0.reverse();
90                let shared_secret = response_sid.shared_secret(nonce_reverse, public_identity);
91                let mut ctx = ChaCha20Poly1305::new(&shared_secret.as_ref(), &nonce.0, &[]);
92                let mut encrypted: Vec<u8> = vec![0; data.len()];
93                let mut tag = Tag([0; TAG_SIZE]);
94                ctx.encrypt(&data, &mut encrypted[..], &mut tag.0);
95                let data = Data::new(&encrypted[..])?;
96                Ok(ResponseData::CypherText { data: Box::new(data), tag })
97            },
98            PublicIdentityInterface::Absent => {
99                if data.len() > DATA_SIZE {
100                    return Err(anyhow!("Ensure data.len()({}) passed into ResponseData::clear_text() is not greater than {}", data.len(), DATA_SIZE))
101                }
102                let mut rng = rand::thread_rng();
103                let length = data.len();
104                //let padding: Vec<u8> = (0..(DATA_SIZE - length)).map(|_| rng.gen()).collect();
105                let padding: Vec<u8> = (0..(DATA_SIZE - length)).map(|_| 0).collect();
106                let b1 = length as u8;
107                let b2 = (length >> 8) as u8;
108                let u8_nonce: u8 = rng.gen();
109                let metadata = vec![u8_nonce, b2, b1];
110                let data = vec![data, padding, metadata];
111                let data = Data::new(&data.into_iter().flatten().collect::<Vec<u8>>()[..])?;
112                Ok(ResponseData::ClearText { data: Box::new(data) })
113            },
114        }
115    }
116    pub fn extract(&self, request_sid: PrivateIdentityInterface, request_pid: PublicIdentityInterface, response_pid: PublicIdentity, nonce: Nonce) -> Result<Vec<u8>> {
117        match self {
118            ResponseData::ClearText { data } => {
119                data.data()
120            },
121            ResponseData::CypherText { data, tag } => {
122                match request_pid {
123                    PublicIdentityInterface::Present { public_identity } => {
124                        if public_identity != request_sid.public_id() {
125                            let err_msg = "The Response's Request_PublicIdentity doesn't match the Public Identity used to sign or decypt the Response";
126                            error!("{}", err_msg);
127                            return Err(anyhow!(err_msg));
128                        }
129                        let mut nonce_reverse = nonce.clone();
130                        nonce_reverse.0.reverse();
131                        let shared_secret = request_sid.shared_secret(nonce_reverse, response_pid);
132                        let mut ctx = ChaCha20Poly1305::new(&shared_secret.as_ref(), &nonce.0, &[]);
133                        let mut decrypted = [0u8; FRAGMENT_SIZE];
134                        if ctx.decrypt(&data.as_bytes(), &mut decrypted[..], &tag.0[..]) {
135                            let data: Data = Data::new(&decrypted[..])?;
136                            Ok(data.data()?)
137                        } else {
138                            Err(anyhow!("Couldn't decrypt the data"))
139                        }
140                    },
141                    PublicIdentityInterface::Absent => Err(anyhow!("Cannot determine if the Request's PublicIdentity matches the PublicIdentity used to sign or decrypt the encrypted Response"))
142                }
143            },
144        }
145    }
146}
147