use crate::error::{VmError, VmResult};
use crate::build_config;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
pub use build_config::MAGIC;
pub const FORMAT_VERSION: u16 = 1;
#[repr(u16)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BytecodeFlags {
None = 0,
Encrypted = 1 << 0,
HasIntegrity = 1 << 1,
HasTimingChecks = 1 << 2,
Paranoid = 1 << 3,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ProtectionLevel {
Debug,
Low,
Medium,
High,
Paranoid,
}
impl ProtectionLevel {
pub fn to_flags(self) -> u16 {
match self {
ProtectionLevel::Debug => 0,
ProtectionLevel::Low => BytecodeFlags::Encrypted as u16,
ProtectionLevel::Medium => {
BytecodeFlags::Encrypted as u16 | BytecodeFlags::HasIntegrity as u16
}
ProtectionLevel::High => {
BytecodeFlags::Encrypted as u16
| BytecodeFlags::HasIntegrity as u16
}
ProtectionLevel::Paranoid => {
BytecodeFlags::Encrypted as u16
| BytecodeFlags::HasIntegrity as u16
| BytecodeFlags::HasTimingChecks as u16
| BytecodeFlags::Paranoid as u16
}
}
}
}
#[derive(Clone, Debug)]
pub struct BytecodeHeader {
pub magic: [u8; 4],
pub version: u16,
pub flags: u16,
pub build_id: u64,
pub timestamp: u64,
pub nonce: [u8; 12],
pub tag: [u8; 16],
pub code_len: u32,
}
impl BytecodeHeader {
pub const SIZE: usize = 4 + 2 + 2 + 8 + 8 + 12 + 16 + 4;
pub fn new(build_id: u64, timestamp: u64, flags: u16) -> Self {
Self {
magic: MAGIC,
version: FORMAT_VERSION,
flags,
build_id,
timestamp,
nonce: [0u8; 12],
tag: [0u8; 16],
code_len: 0,
}
}
pub fn to_bytes(&self) -> [u8; Self::SIZE] {
let mut buf = [0u8; Self::SIZE];
let mut offset = 0;
buf[offset..offset + 4].copy_from_slice(&self.magic);
offset += 4;
buf[offset..offset + 2].copy_from_slice(&self.version.to_le_bytes());
offset += 2;
buf[offset..offset + 2].copy_from_slice(&self.flags.to_le_bytes());
offset += 2;
buf[offset..offset + 8].copy_from_slice(&self.build_id.to_le_bytes());
offset += 8;
buf[offset..offset + 8].copy_from_slice(&self.timestamp.to_le_bytes());
offset += 8;
buf[offset..offset + 12].copy_from_slice(&self.nonce);
offset += 12;
buf[offset..offset + 16].copy_from_slice(&self.tag);
offset += 16;
buf[offset..offset + 4].copy_from_slice(&self.code_len.to_le_bytes());
buf
}
pub fn from_bytes(data: &[u8]) -> VmResult<Self> {
if data.len() < Self::SIZE {
return Err(VmError::InvalidBytecode);
}
let mut offset = 0;
let mut magic = [0u8; 4];
magic.copy_from_slice(&data[offset..offset + 4]);
if magic != MAGIC {
return Err(VmError::InvalidBytecode);
}
offset += 4;
let version = u16::from_le_bytes([data[offset], data[offset + 1]]);
if version > FORMAT_VERSION {
return Err(VmError::InvalidBytecode);
}
offset += 2;
let flags = u16::from_le_bytes([data[offset], data[offset + 1]]);
offset += 2;
let build_id = u64::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
offset += 8;
let timestamp = u64::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
data[offset + 4],
data[offset + 5],
data[offset + 6],
data[offset + 7],
]);
offset += 8;
let mut nonce = [0u8; 12];
nonce.copy_from_slice(&data[offset..offset + 12]);
offset += 12;
let mut tag = [0u8; 16];
tag.copy_from_slice(&data[offset..offset + 16]);
offset += 16;
let code_len = u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
]);
Ok(Self {
magic,
version,
flags,
build_id,
timestamp,
nonce,
tag,
code_len,
})
}
pub fn is_encrypted(&self) -> bool {
self.flags & BytecodeFlags::Encrypted as u16 != 0
}
pub fn has_integrity(&self) -> bool {
self.flags & BytecodeFlags::HasIntegrity as u16 != 0
}
pub fn has_timing_checks(&self) -> bool {
self.flags & BytecodeFlags::HasTimingChecks as u16 != 0
}
pub fn is_paranoid(&self) -> bool {
self.flags & BytecodeFlags::Paranoid as u16 != 0
}
}
#[derive(Clone, Debug)]
pub struct BytecodePackage {
pub header: BytecodeHeader,
pub code: Vec<u8>,
}
impl BytecodePackage {
pub fn new_plaintext(code: Vec<u8>, build_id: u64) -> Self {
let mut header = BytecodeHeader::new(build_id, 0, 0);
header.code_len = code.len() as u32;
Self { header, code }
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut buf = Vec::with_capacity(BytecodeHeader::SIZE + self.code.len());
buf.extend_from_slice(&self.header.to_bytes());
buf.extend_from_slice(&self.code);
buf
}
pub fn from_bytes(data: &[u8]) -> VmResult<Self> {
let header = BytecodeHeader::from_bytes(data)?;
let code_start = BytecodeHeader::SIZE;
let code_end = code_start + header.code_len as usize;
if data.len() < code_end {
return Err(VmError::InvalidBytecode);
}
let code = data[code_start..code_end].to_vec();
Ok(Self { header, code })
}
}
#[derive(Clone, Debug)]
pub struct BuildInfo {
pub build_id: u64,
pub timestamp: u64,
pub git_commit: u64,
pub protection_level: ProtectionLevel,
}
impl BuildInfo {
pub fn new(build_id: u64, protection_level: ProtectionLevel) -> Self {
Self {
build_id,
timestamp: 0, git_commit: 0,
protection_level,
}
}
}