koibumi-core 0.0.9

The core library for Koibumi, an experimental Bitmessage client
Documentation
use std::{
    convert::TryFrom,
    fmt,
    io::{self, Cursor, Read, Write},
};

use crate::{
    content, crypto,
    identity::Private as PrivateIdentity,
    io::{ReadFrom, SizedReadFrom, WriteTo},
    message,
    object::{DecryptError, Header, ObjectKind, ObjectType, ObjectVersion, TryFromObjectTypeError},
    var_type::VarInt,
};

/// msg object.
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Msg {
    encrypted: Vec<u8>,
}

impl Msg {
    /// Creates a msg object from an encrypted content.
    pub fn new(encrypted: Vec<u8>) -> Self {
        Self { encrypted }
    }

    /// Returns the reference to the encrypted content of this msg object.
    pub fn encrypted(&self) -> &[u8] {
        &self.encrypted
    }

    fn signed_header(&self, header: &Header) -> Result<Vec<u8>, io::Error> {
        let mut bytes = Vec::new();
        header.expires_time().write_to(&mut bytes)?;
        header.object_type().write_to(&mut bytes)?;
        VarInt::new(1).write_to(&mut bytes)?;
        header.stream_number().write_to(&mut bytes)?;
        Ok(bytes)
    }

    /// Decrypts this msg object and returns the decrypted content.
    pub fn decrypt(
        &self,
        header: &Header,
        identity: &PrivateIdentity,
    ) -> Result<content::Msg, DecryptError> {
        let mut bytes = Cursor::new(self.encrypted());
        let encrypted = crypto::Encrypted::sized_read_from(&mut bytes, self.encrypted().len())?;
        let private_key = identity.private_encryption_key();
        let decrypted = encrypted.decrypt(&private_key)?;
        let mut bytes = Cursor::new(decrypted);
        let content = content::Msg::read_from(&mut bytes)?;

        if content.stream_number() != header.stream_number() {
            return Err(DecryptError::StreamsNotMatch {
                headers: header.stream_number(),
                contents: content.stream_number(),
            });
        }
        // TODO check validity

        content.verify(self.signed_header(header)?)?;

        Ok(content)
    }
}

impl WriteTo for Msg {
    fn write_to(&self, w: &mut dyn Write) -> io::Result<()> {
        self.encrypted.write_to(w)?;
        Ok(())
    }
}

impl SizedReadFrom for Msg {
    fn sized_read_from(r: &mut dyn Read, len: usize) -> io::Result<Self>
    where
        Self: Sized,
    {
        let encrypted = Vec::<u8>::sized_read_from(r, len)?;
        Ok(Self { encrypted })
    }
}

/// This error indicates
/// that the conversion from object to msg failed.
#[derive(Debug)]
pub enum TryIntoMsgError {
    /// Indicates that the type of the supplied object was not msg.
    /// The actual type of the object is returned as a payload of this variant.
    InvalidType(ObjectType),
    /// A standard I/O error was caught during decrypting a message.
    /// The actual error caught is returned as a payload of this variant.
    IoError(io::Error),
    /// Indicates that the version of the supplied object was not supported.
    /// The actual version of the object is returned as a payload of this variant.
    UnsupportedVersion(ObjectVersion),
}

impl fmt::Display for TryIntoMsgError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::InvalidType(object_type) => write!(f, "invalid object type: {}", object_type),
            Self::IoError(err) => err.fmt(f),
            Self::UnsupportedVersion(version) => {
                write!(f, "unsupported broadcast version: {}", version)
            }
        }
    }
}

impl std::error::Error for TryIntoMsgError {}

impl From<io::Error> for TryIntoMsgError {
    fn from(err: io::Error) -> Self {
        Self::IoError(err)
    }
}

impl TryFrom<message::Object> for Msg {
    type Error = TryIntoMsgError;

    fn try_from(
        object: message::Object,
    ) -> Result<Self, <Self as TryFrom<message::Object>>::Error> {
        let kind = ObjectKind::try_from(object.header().object_type());
        if let Err(TryFromObjectTypeError(object_type)) = kind {
            return Err(TryIntoMsgError::InvalidType(object_type));
        }
        if kind.unwrap() != ObjectKind::Msg {
            return Err(TryIntoMsgError::InvalidType(object.header().object_type()));
        }
        let mut bytes = Cursor::new(object.object_payload());
        let msg = Msg::sized_read_from(&mut bytes, object.object_payload().len())?;
        Ok(msg)
    }
}