tea-actorx2-core 0.2.0-dev.16

The TEA SDK
Documentation
use error::{InvalidSignatureFormat, Result, SignatureMismatch};
use openssl::{
	hash::MessageDigest,
	pkey::PKey,
	sign::{Signer, Verifier},
};
use serde::{Deserialize, Serialize};

use super::metadata::Metadata;
pub mod error;

extern crate tea_codec as tea_sdk;

const WASM_HEAD_LENGTH: usize = 8;

const CURRENT_VERSION: u32 = 1;

const SECTION_NAME: &[u8] = b"\tSignature";

#[derive(Debug, Clone, Serialize, Deserialize)]
struct Metatoken {
	version: u32,
	payload: Vec<u8>,
	signature: Vec<u8>,
}

pub fn sign(wasm: &mut Vec<u8>, mut data: Metadata) -> Result<()> {
	let key = PKey::private_key_from_pem(&data.signer)
		.or_else(|_| PKey::private_key_from_der(&data.signer))?;
	data.signer = key.public_key_to_der()?;

	let payload = tea_codec::serialize(&data)?;

	let mut signer = Signer::new(MessageDigest::sha256(), &key)?;
	signer.update(&wasm[0..WASM_HEAD_LENGTH])?;
	signer.update(&wasm[WASM_HEAD_LENGTH..])?;
	signer.update(&payload)?;

	let signature = signer.sign_to_vec()?;

	let token = Metatoken {
		version: CURRENT_VERSION,
		payload,
		signature,
	};

	let token = tea_codec::serialize(&token)?;

	let mut token = &token[..];

	let token = zstd::encode_all(&mut token, zstd::zstd_safe::max_c_level())?;

	let mut data_len = [0; 5];
	let count = leb128::write::unsigned(
		&mut &mut data_len[..],
		(token.len() + SECTION_NAME.len()) as _,
	)?;
	let data_len = data_len.into_iter().take(count);

	let token = Some(0)
		.into_iter()
		.chain(data_len)
		.chain(SECTION_NAME.iter().cloned())
		.chain(token);

	wasm.splice(WASM_HEAD_LENGTH..WASM_HEAD_LENGTH, token);

	Ok(())
}

pub fn verify(wasm: &[u8]) -> Result<Metadata> {
	let (head, mut token) = wasm.split_at(WASM_HEAD_LENGTH);
	token = &token[1..];
	let data_len = leb128::read::unsigned(&mut token)?;

	let (token, rest) = token.split_at(data_len as _);

	if !token.starts_with(SECTION_NAME) {
		return Err(InvalidSignatureFormat.into());
	}

	let token = &token[SECTION_NAME.len()..];

	let token = zstd::decode_all(token)?;

	let token: Metatoken = tea_codec::deserialize(token)?;

	let payload: Metadata = tea_codec::deserialize(&token.payload)?;

	let key = PKey::public_key_from_der(&payload.signer)?;

	let mut signer = Verifier::new(MessageDigest::sha256(), &key)?;
	signer.update(head)?;
	signer.update(rest)?;
	signer.update(&token.payload)?;

	if signer.verify(&token.signature)? {
		Ok(payload)
	} else {
		Err(SignatureMismatch.into())
	}
}