1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
//! Wire provides a container type to map byte data to fixed fields (and vice versa)
//! to support wire encoding and decoding.

#[cfg(feature = "alloc")]
use alloc::prelude::v1::*;

use crate::base::{BaseError, Body, Header, PrivateOptions};
use crate::crypto;
use crate::options::{Options, OptionsList};
use crate::types::*;

use crate::base::Base;

/// Header provides a low-cost header abstraction for encoding/decoding
pub mod header;

/// Builder provides methods to construct a container using a mutable buffer and base types
pub mod builder;
pub use builder::Builder;

/// Container provides methods to access underlying wire object fields
pub mod container;
pub use container::Container;

impl<'a, T: AsRef<[u8]>> Container<T> {
    /// Parses a data array into a base object using the pub_key and sec_key functions to locate
    /// keys for validation and decyption
    pub fn parse<P, S>(
        data: T,
        mut pub_key_s: P,
        mut sec_key_s: S,
    ) -> Result<(Base, usize), BaseError>
    where
        P: FnMut(&Id) -> Option<PublicKey>,
        S: FnMut(&Id) -> Option<SecretKey>,
    {
        let mut verified = false;

        // Build container over buffer
        let (container, n) = Container::from(data);
        let header = container.header();

        // Fetch page flags
        let flags = header.flags();

        // Fetch page ID
        let id: Id = container.id().into();

        // Fetch signature for page
        let signature: Signature = container.signature().into();

        // Validate primary types immediately if pubkey is known
        if !flags.contains(Flags::SECONDARY) {
            // Lookup public key
            if let Some(key) = (pub_key_s)(&id) {
                // Check ID matches key
                if id != crypto::hash(&key).unwrap() {
                    return Err(BaseError::PublicKeyIdMismatch);
                }

                // Validate message body against key
                verified = crypto::pk_validate(&key, &signature, container.signed())
                    .map_err(|_e| BaseError::ValidateError)?;

                // Stop processing if signature is invalid
                if !verified {
                    info!("Invalid signature with known pubkey");
                    return Err(BaseError::InvalidSignature);
                }
            }
        }

        // Fetch public options
        let mut peer_id = None;
        let mut pub_key = None;
        let mut parent = None;

        let public_options: Vec<_> = container
            .public_options()
            .filter_map(|o| match &o {
                Options::PeerId(v) => {
                    peer_id = Some(v.peer_id.clone());
                    None
                }
                Options::PubKey(v) => {
                    pub_key = Some(v.public_key.clone());
                    None
                }
                Options::PrevSig(v) => {
                    parent = Some(v.sig.clone());
                    None
                }
                _ => Some(o),
            })
            .collect();

        // Look for signing ID
        let signing_id: Id = match (flags.contains(Flags::SECONDARY), &peer_id) {
            (false, _) => Ok(container.id().into()),
            (true, Some(id)) => Ok(id.clone()),
            _ => Err(BaseError::NoPeerId),
        }?;

        // Fetch public key
        let public_key: Option<PublicKey> = match ((pub_key_s)(&signing_id), &pub_key) {
            (Some(key), _) => Some(key),
            (None, Some(key)) => Some(key.clone()),
            _ => {
                warn!(
                    "Missing public key for message: {:?} signing id: {:?}",
                    id, signing_id
                );
                None
            }
        };

        // Late validation for self-signed objects from unknown sources
        match (verified, public_key) {
            (false, Some(public_key)) => {
                // Check ID matches key
                if signing_id != crypto::hash(&public_key).unwrap() {
                    error!("Public key mismatch for object from {:?}", id);
                    return Err(BaseError::PublicKeyIdMismatch);
                }

                // Verify body
                verified = crypto::pk_validate(&public_key, &signature, container.signed())
                    .map_err(|_e| BaseError::ValidateError)?;

                // Stop processing on verification error
                if !verified {
                    info!("Invalid signature for self-signed object from {:?}", id);
                    return Err(BaseError::InvalidSignature);
                }
            }
            (false, None) => {
                error!("No signature or key for object from {:?}", id);
                return Err(BaseError::ValidateError.into());
            }
            _ => (),
        }

        let mut body_data = container.body().to_vec();
        let mut private_options_data = container.private_options().to_vec();

        // Handle body decryption or parsing
        let body = match (flags.contains(Flags::ENCRYPTED), sec_key_s(&id)) {
            (true, Some(sk)) if body_data.len() > 0 => {
                // Decrypt body
                let n = crypto::sk_decrypt2(&sk, &mut body_data)
                    .map_err(|_e| BaseError::InvalidSignature)?;
                body_data = (&body_data[..n]).to_vec();

                Body::Cleartext(body_data)
            }
            (true, None) if body_data.len() > 0 => {
                debug!("No encryption key found for data");

                Body::Encrypted(body_data)
            }
            (false, _) if body_data.len() > 0 => Body::Cleartext(body_data),
            _ => Body::None,
        };

        // Handle private_options decryption or parsing
        let private_options = match (flags.contains(Flags::ENCRYPTED), sec_key_s(&id)) {
            (true, Some(sk)) if private_options_data.len() > 0 => {
                // Decrypt private options
                let n = crypto::sk_decrypt2(&sk, &mut private_options_data)
                    .map_err(|_e| BaseError::InvalidSignature)?;
                private_options_data = (&private_options_data[..n]).to_vec();

                // Decode private options
                let (private_options, _n) = Options::parse_vec(&private_options_data)?;

                PrivateOptions::Cleartext(private_options)
            }
            (true, None) if private_options_data.len() > 0 => {
                debug!("No encryption key found for data");

                PrivateOptions::Encrypted(private_options_data)
            }
            _ => PrivateOptions::None,
        };

        // Return page and options
        Ok((
            Base {
                id,
                header: Header::new(
                    header.application_id(),
                    header.kind(),
                    header.index(),
                    header.flags(),
                ),
                body,

                private_options,
                public_options,

                parent,
                peer_id: peer_id.clone(),
                public_key: pub_key.clone(),

                signature: Some(signature),
                verified,

                raw: Some(container.raw().to_vec()),
            },
            n,
        ))
    }
}

impl<'a, T: AsRef<[u8]> + AsMut<[u8]>> Container<T> {
    pub fn encode(
        buff: T,
        base: &Base,
        signing_key: &PrivateKey,
        encryption_key: Option<&SecretKey>,
    ) -> (Self, usize) {
        // Setup base builder with header and ID
        let bb = Builder::new(buff).id(base.id()).header(base.header());

        // Check encryption key exists if required
        let encryption_key = match (base.flags().contains(Flags::ENCRYPTED), encryption_key) {
            (true, Some(k)) => Some(k),
            (true, None) => panic!("Attempted to encrypt object with no secret key"),
            _ => None,
        };

        // Write body
        let bb = bb.body(base.body(), encryption_key).unwrap();

        // Write private options
        let mut bb = bb
            .private_options(base.private_options(), encryption_key)
            .unwrap();

        // Write public options
        // Add public key option if specified
        if let Some(k) = &base.public_key {
            bb.public_option(&Options::pub_key(k.clone())).unwrap();
        }

        if let Some(s) = &base.parent {
            bb.public_option(&Options::prev_sig(s)).unwrap();
        }

        if let Some(i) = &base.peer_id {
            bb.public_option(&Options::peer_id(i.clone())).unwrap();
        }

        let opts = OptionsList::<_, &[u8]>::Cleartext(base.public_options());
        let bb = bb.public_options(&opts).unwrap();

        // Sign object
        let c = bb.sign(signing_key).unwrap();
        let len = c.len;

        (c, len)
    }
}

#[cfg(test)]
mod test {}