java-serialization 0.1.0

Java object serialization stream parser written in Rust
Documentation
use crate::constants;

/// Parsed Java serialization stream.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
pub struct SerializationStream {
    pub version: u16,
    pub contents: Vec<ContentElement>,
}

/// Top-level content elements in a serialization stream.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
pub enum ContentElement {
    Object(StreamObject),
    BlockData(BlockData),
}

/// An object in the serialization stream.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
pub enum StreamObject {
    NewObject(NewObject),
    NewClass(NewClass),
    NewArray(NewArray),
    NewString(StreamString),
    NewEnum(NewEnum),
    NewClassDesc(ClassDesc),
    PrevObject { handle: u32 },
    NullReference,
    Exception,
    Reset,
}

/// TC_OBJECT: new object.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
pub struct NewObject {
    pub class_desc: ClassDescRef,
    pub handle: u32,
    pub class_data: Vec<ClassData>,
}

/// TC_CLASS: reference to a class.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
pub struct NewClass {
    pub class_desc: ClassDescRef,
    pub handle: u32,
}

/// TC_ARRAY: new array.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
pub struct NewArray {
    pub class_desc: ClassDescRef,
    pub handle: u32,
    pub size: u32,
    pub values: ArrayValues,
}

/// Array values based on component type.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
pub enum ArrayValues {
    Byte(Vec<i8>),
    Char(Vec<u16>),
    Double(Vec<f64>),
    Float(Vec<f32>),
    Int(Vec<i32>),
    Long(Vec<i64>),
    Short(Vec<i16>),
    Boolean(Vec<u8>),
    Object(Vec<Option<StreamObject>>),
}

/// TC_STRING / TC_LONGSTRING.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
pub struct StreamString {
    pub value: String,
    pub handle: u32,
    pub is_long: bool,
}

/// TC_ENUM: new enum constant.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
pub struct NewEnum {
    pub class_desc: ClassDescRef,
    pub handle: u32,
    pub constant_name: StreamString,
}

/// Reference to a class descriptor - either inline or a back-reference.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
pub enum ClassDescRef {
    /// Inline class descriptor.
    Inline(Box<ClassDesc>),
    /// Null reference (no superclass).
    Null,
    /// Back-reference to a previously seen class descriptor by handle.
    Reference { handle: u32 },
}

/// Class descriptor: either a normal class or a proxy class.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
pub enum ClassDesc {
    Normal(Box<NormalClassDesc>),
    Proxy(ProxyClassDesc),
}

/// TC_CLASSDESC: normal class descriptor.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
pub struct NormalClassDesc {
    pub class_name: String,
    pub serial_version_uid: i64,
    pub handle: u32,
    pub flags: u8,
    pub fields: Vec<FieldDesc>,
    pub class_annotation: Vec<AnnotationElement>,
    pub super_class_desc: Box<ClassDescRef>,
}

/// TC_PROXYCLASSDESC: proxy class descriptor.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
pub struct ProxyClassDesc {
    pub handle: u32,
    pub interface_names: Vec<String>,
    pub class_annotation: Vec<AnnotationElement>,
    pub super_class_desc: Box<ClassDescRef>,
}

/// Field descriptor in a class descriptor.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
pub enum FieldDesc {
    Primitive(PrimitiveFieldDesc),
    Object(ObjectFieldDesc),
}

/// Primitive field descriptor.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
pub struct PrimitiveFieldDesc {
    pub type_code: u8,
    pub field_name: String,
}

/// Object field descriptor.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
pub struct ObjectFieldDesc {
    pub type_code: u8,
    pub field_name: String,
    pub class_name: String,
}

/// Elements in class annotation (between classDescInfo and superClassDesc).
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
pub enum AnnotationElement {
    Object(StreamObject),
    BlockData(BlockData),
}

/// Block data.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
pub enum BlockData {
    Short { data: Vec<u8> },
    Long { data: Vec<u8> },
}

/// Class data for one class level in an object.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
pub enum ClassData {
    /// SC_SERIALIZABLE without SC_WRITE_METHOD: just field values.
    NoWriteMethod(FieldValueSet),
    /// SC_SERIALIZABLE with SC_WRITE_METHOD, and defaultWriteObject() was called:
    /// field values followed by object annotation.
    WriteMethodWithFields(FieldValueSet, ObjectAnnotation),
    /// SC_SERIALIZABLE with SC_WRITE_METHOD, and defaultWriteObject() was NOT called:
    /// entire classdata is an objectAnnotation.
    WriteMethod(ObjectAnnotation),
    /// SC_EXTERNALIZABLE without SC_BLOCK_DATA: raw external contents.
    ExternalContents(Vec<u8>),
    /// SC_EXTERNALIZABLE with SC_BLOCK_DATA: object annotation format.
    ExternalBlockData(ObjectAnnotation),
}

/// Set of field values for a class.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
pub struct FieldValueSet {
    pub values: Vec<FieldValue>,
}

/// A single field value.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
pub enum FieldValue {
    Byte(i8),
    Char(u16),
    Double(f64),
    Float(f32),
    Int(i32),
    Long(i64),
    Short(i16),
    Boolean(bool),
    Object(Option<Box<StreamObject>>),
}

/// Object annotation: contents between the class data and TC_ENDBLOCKDATA.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
pub struct ObjectAnnotation {
    pub contents: Vec<AnnotationElement>,
}

impl NormalClassDesc {
    pub fn has_write_method(&self) -> bool {
        self.flags & constants::SC_WRITE_METHOD != 0
    }

    pub fn is_serializable(&self) -> bool {
        self.flags & constants::SC_SERIALIZABLE != 0
    }

    pub fn is_externalizable(&self) -> bool {
        self.flags & constants::SC_EXTERNALIZABLE != 0
    }

    pub fn has_block_data(&self) -> bool {
        self.flags & constants::SC_BLOCK_DATA != 0
    }

    pub fn is_enum(&self) -> bool {
        self.flags & constants::SC_ENUM != 0
    }
}

impl BlockData {
    /// Get the raw data bytes from block data.
    pub fn data(&self) -> &[u8] {
        match self {
            BlockData::Short { data } => data,
            BlockData::Long { data } => data,
        }
    }
}

impl ClassDescRef {
    /// Try to get the class name from this class descriptor reference.
    /// Returns None for Null references and unresolved Reference types.
    pub fn class_name(&self) -> Option<&str> {
        match self {
            ClassDescRef::Inline(cd) => cd.class_name(),
            ClassDescRef::Null => None,
            ClassDescRef::Reference { .. } => None,
        }
    }
}

impl ClassDesc {
    /// Get the class name from this class descriptor.
    pub fn class_name(&self) -> Option<&str> {
        match self {
            ClassDesc::Normal(desc) => Some(&desc.class_name),
            ClassDesc::Proxy(_) => None,
        }
    }
}

impl NewObject {
    /// Get the class name of this object, if resolvable.
    pub fn class_name(&self) -> Option<&str> {
        self.class_desc.class_name()
    }
}

impl NewArray {
    /// Get the array class name (e.g., "[B", "[Ljava/lang/Object;").
    pub fn class_name(&self) -> Option<&str> {
        self.class_desc.class_name()
    }
}

impl StreamObject {
    /// Get the handle assigned to this object, if any.
    pub fn handle(&self) -> Option<u32> {
        match self {
            StreamObject::NewObject(o) => Some(o.handle),
            StreamObject::NewClass(c) => Some(c.handle),
            StreamObject::NewArray(a) => Some(a.handle),
            StreamObject::NewString(s) => Some(s.handle),
            StreamObject::NewEnum(e) => Some(e.handle),
            StreamObject::NewClassDesc(ClassDesc::Normal(d)) => Some(d.handle),
            StreamObject::NewClassDesc(ClassDesc::Proxy(d)) => Some(d.handle),
            StreamObject::PrevObject { handle } => Some(*handle),
            StreamObject::NullReference | StreamObject::Exception | StreamObject::Reset => None,
        }
    }
}

impl SerializationStream {
    /// Iterate over all top-level objects in the stream.
    pub fn objects(&self) -> impl Iterator<Item = &StreamObject> {
        self.contents.iter().filter_map(|c| match c {
            ContentElement::Object(obj) => Some(obj),
            ContentElement::BlockData(_) => None,
        })
    }
}