foxmark3 0.1.1

Send/receive Proxmark 3 commands
Documentation
//! Commands and structures for [`Proxmark::capabilities`].

use std::time::Duration;

use bytemuck::from_bytes;

use crate::raw::{self, Command, device::Error};

use super::Proxmark;

/// The latest firmware version, which is also the firmware version `foxmark3` has been
/// developed against, and thus the firmware version to expect in a [`Capabilities`].
pub const FIRMWARE_VERSION: u8 = 6;

mod features {
    #![allow(missing_docs)]

    bitflags::bitflags! {
        /// Features supported by a device firmware.
        ///
        /// For the official client, these bitflags are meant to indicate what commands the
        /// client may wish to call when performing an abstract task. For example, `hf info`
        /// tries reading an ISO/IEC 14443 type A if the corresponding bitflag is `true`.
        #[repr(transparent)]
        #[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
        pub struct Features: u32 {
            const ViaFpc = 1 << 0;
            const ViaUsb = 1 << 1;
            const CompiledWithFlash = 1 << 2;
            const CompiledWithSmartcard = 1 << 3;
            const CompiledWithFpcUsart = 1 << 4;
            const CompiledWithFpcUsartDev = 1 << 5;
            const CompiledWithFpcUsartHost = 1 << 6;
            const CompiledWithLf = 1 << 7;
            const CompiledWithHitag = 1 << 8;
            const CompiledWithEm4x50 = 1 << 9;
            const CompiledWithEm4x70 = 1 << 10;
            const CompiledWithZx8211 = 1 << 11;
            const CompiledWithHfSniff = 1 << 12;
            const CompiledWithHfPlot = 1 << 13;
            const CompiledWithIso14443a = 1 << 14;
            const CompiledWithIso14443b = 1 << 15;
            const CompiledWithIso15693 = 1 << 16;
            const CompiledWithFelica = 1 << 17;
            const CompiledWithLegicRf = 1 << 18;
            const CompiledWithIClass = 1 << 19;
            const CompiledWithNfcBarcode = 1 << 20;
            const CompiledWithLcd = 1 << 21;
            const HwAvailableFlash = 1 << 22;
            const HwAvailableSmartcard = 1 << 23;
            const IsRdv4 = 1 << 24;
        }
    }
}

pub use features::Features;

// [include/pm3_cmd.h:199-234]
/// The capabilities of a device firmware, reported by itself.
///
/// [`Proxmark::capabilities`] requests this data.
#[derive(Debug, Copy, Clone)]
pub struct Capabilities {
    /// The version of the firmware. This should nominally be [`FIRMWARE_VERSION`].
    pub version: u8,
    /// The baudrate of the connection.
    pub baudrate: u32,
    /// The size of big buffers supported. This value isn't too helpful except for when the
    /// connection speed is being checked.
    pub bigbuf_size: u32,
    /// The features supported by the device firmware.
    pub features: Features,
}

/// An error produced by [`Proxmark::capabilities`].
#[derive(Debug, thiserror::Error)]
pub enum CapabilitiesError {
    /// The structure sent by the device did not have the size expected.
    #[error(
        "device sent capabilities with unexpected length (expected: {expected}, got: {got}, device version: {version:?})"
    )]
    LengthMismatch {
        /// The expected byte size of the capabilities information.
        expected: usize,
        /// The  byte size of the capabilities information which was sent from the device.
        got: usize,
        /// The version of the firmware. Note that this may not be accurate, as the device could
        /// have sent garbage data in the first place, but the information may be helpful to report
        /// to the user regardless.
        version: Option<u8>,
    },
    /// The version reported by the device was not as expected [`FIRMWARE_VERSION`]..
    #[error(
        "device reported an unexpected firmware version (expected: {FIRMWARE_VERSION}, got {})", .0.version
    )]
    VersionMismatch(Capabilities),
    /// The error originated from the [`Proxmark`].
    #[error(transparent)]
    Proxmark(#[from] Error),
}

impl Proxmark {
    /// Obtains the firmware capabilities of the device.
    #[tracing::instrument(skip(self), level = tracing::Level::TRACE)]
    pub fn capabilities(&mut self) -> Result<Capabilities, CapabilitiesError> {
        // [src/comms.c:883-923]

        self.0
            .request(raw::request::ng(Command::CAPABILITIES, []))
            .map_err(Error::from)?;

        let resp = self
            .0
            .response_of(Command::CAPABILITIES, Duration::from_secs(1))
            .map_err(Error::from)?;

        // [include/pm3_cmd.h:199-234]
        #[repr(C, packed)]
        #[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
        struct PackedCapabilities {
            version: u8,
            baudrate: u32,
            bigbuf_size: u32,
            features: Features,
        }

        if resp.payload().len() != size_of::<PackedCapabilities>() {
            return Err(CapabilitiesError::LengthMismatch {
                expected: size_of::<PackedCapabilities>(),
                got: resp.payload().len(),
                version: resp.payload().first().copied(),
            });
        }

        let packed = from_bytes::<PackedCapabilities>(resp.payload());
        let unpacked = Capabilities {
            version: packed.version,
            baudrate: packed.baudrate,
            bigbuf_size: packed.bigbuf_size,
            features: packed.features,
        };

        if unpacked.version != FIRMWARE_VERSION {
            return Err(CapabilitiesError::VersionMismatch(unpacked));
        }

        Ok(unpacked)
    }
}