koibumi-core 0.0.9

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

use crate::{
    address::{Address, Error as AddressError, Stream, Version as AddressVersion},
    crypto::{self, PublicKey, Signature, VerifyError},
    encoding::Encoding,
    error::TooLongError,
    feature::Features,
    identity::Private as PrivateIdentity,
    io::{ReadFrom, SizedReadFrom, WriteTo},
    message,
    pow::{NonceTrialsPerByte, PayloadLengthExtraBytes},
    var_type::VarInt,
};

use crate::crypto::SignError;

/// An error can be returned when processing a broadcast content.
#[derive(Clone, Debug)]
pub enum BroadcastError {
    /// Indicates that the signing failed.
    SignError(SignError),
}

impl fmt::Display for BroadcastError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::SignError(err) => err.fmt(f),
        }
    }
}

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

impl From<SignError> for BroadcastError {
    fn from(err: SignError) -> Self {
        Self::SignError(err)
    }
}

/// A content of a broadcast message.
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Broadcast {
    address_version: AddressVersion,
    stream_number: Stream,
    behavior_bitfield: Features,
    public_signing_key: PublicKey,
    public_encryption_key: PublicKey,
    nonce_trials_per_byte: NonceTrialsPerByte,
    extra_bytes: PayloadLengthExtraBytes,
    encoding: Encoding,
    message: Vec<u8>,
    signature: Signature,
}

impl Broadcast {
    /// Construct a broadcast content and sign it.
    pub fn new(
        header: impl AsRef<[u8]>,
        identity: &PrivateIdentity,
        encoding: Encoding,
        message: Vec<u8>,
    ) -> Result<Self, BroadcastError> {
        let address_version = identity.version();
        let stream_number = identity.stream();
        let behavior_bitfield = identity.features();
        let public_signing_key = identity.public_signing_key();
        let public_encryption_key = identity.public_encryption_key();
        let nonce_trials_per_byte = identity.nonce_trials_per_byte();
        let extra_bytes = identity.payload_length_extra_bytes();

        let mut data = header.as_ref().to_vec();
        address_version.write_to(&mut data).unwrap();
        stream_number.write_to(&mut data).unwrap();
        behavior_bitfield.write_to(&mut data).unwrap();
        public_signing_key.write_to(&mut data).unwrap();
        public_encryption_key.write_to(&mut data).unwrap();
        if address_version.as_u64() >= 3 {
            nonce_trials_per_byte.write_to(&mut data).unwrap();
            extra_bytes.write_to(&mut data).unwrap();
        }
        encoding.write_to(&mut data).unwrap();
        let len = VarInt::from(message.len());
        len.write_to(&mut data).unwrap();
        message.write_to(&mut data).unwrap();
        let signature = crypto::sign(&data, identity.private_signing_key())?;

        Ok(Self {
            address_version,
            stream_number,
            behavior_bitfield,
            public_signing_key,
            public_encryption_key,
            nonce_trials_per_byte,
            extra_bytes,
            encoding,
            message,
            signature,
        })
    }

    /// Returns the stream number.
    pub fn stream_number(&self) -> Stream {
        self.stream_number
    }

    /// Returns the encoding.
    pub fn encoding(&self) -> Encoding {
        self.encoding
    }

    /// Returns the message.
    pub fn message(&self) -> &[u8] {
        &self.message
    }

    /// Verify the signature.
    pub fn verify(&self, signed_header: impl AsRef<[u8]>) -> Result<(), VerifyError> {
        let mut data = signed_header.as_ref().to_vec();
        self.address_version.write_to(&mut data).unwrap();
        self.stream_number.write_to(&mut data).unwrap();
        self.behavior_bitfield.write_to(&mut data).unwrap();
        self.public_signing_key.write_to(&mut data).unwrap();
        self.public_encryption_key.write_to(&mut data).unwrap();
        if self.address_version.as_u64() >= 3 {
            self.nonce_trials_per_byte.write_to(&mut data).unwrap();
            self.extra_bytes.write_to(&mut data).unwrap();
        }
        self.encoding.write_to(&mut data).unwrap();
        let len = VarInt::from(self.message.len());
        len.write_to(&mut data).unwrap();
        self.message.write_to(&mut data).unwrap();
        crypto::verify(&data, &self.signature, &self.public_signing_key)
    }

    /// Returns the Bitmessage address.
    pub fn address(&self) -> Result<Address, AddressError> {
        Ok(Address::from_public_keys(
            self.address_version,
            self.stream_number,
            &self.public_signing_key,
            &self.public_encryption_key,
        )?)
    }
}

impl WriteTo for Broadcast {
    fn write_to(&self, w: &mut dyn Write) -> io::Result<()> {
        self.address_version.write_to(w)?;
        self.stream_number.write_to(w)?;
        self.behavior_bitfield.write_to(w)?;
        self.public_signing_key.write_to(w)?;
        self.public_encryption_key.write_to(w)?;
        if self.address_version.as_u64() >= 3 {
            self.nonce_trials_per_byte.write_to(w)?;
            self.extra_bytes.write_to(w)?;
        }
        self.encoding.write_to(w)?;
        let len = VarInt::from(self.message.len());
        len.write_to(w)?;
        self.message.write_to(w)?;
        self.signature.write_to(w)?;
        Ok(())
    }
}

#[allow(clippy::useless_let_if_seq)]
impl ReadFrom for Broadcast {
    fn read_from(r: &mut dyn Read) -> io::Result<Self>
    where
        Self: Sized,
    {
        let address_version = AddressVersion::read_from(r)?;
        let stream_number = Stream::read_from(r)?;
        let behavior_bitfield = Features::read_from(r)?;
        let public_signing_key = PublicKey::read_from(r)?;
        let public_encryption_key = PublicKey::read_from(r)?;
        let nonce_trials_per_byte;
        let extra_bytes;
        if address_version.as_u64() >= 3 {
            nonce_trials_per_byte = NonceTrialsPerByte::read_from(r)?;
            extra_bytes = PayloadLengthExtraBytes::read_from(r)?;
        } else {
            nonce_trials_per_byte = 1000.into();
            extra_bytes = 1000.into();
        }
        let encoding = Encoding::read_from(r)?;
        let len = VarInt::read_from(r)?;
        if len.as_u64() > message::Object::MAX_OBJECT_PAYLOAD_LENGTH as u64 {
            return Err(io::Error::new(
                io::ErrorKind::Other,
                TooLongError::new(
                    message::Object::MAX_OBJECT_PAYLOAD_LENGTH,
                    len.as_u64() as usize,
                ),
            ));
        }
        let message = Vec::<u8>::sized_read_from(r, len.as_u64() as usize)?;
        let signature = Signature::read_from(r)?;
        Ok(Self {
            address_version,
            stream_number,
            behavior_bitfield,
            public_signing_key,
            public_encryption_key,
            nonce_trials_per_byte,
            extra_bytes,
            encoding,
            message,
            signature,
        })
    }
}