use std::cmp;
use anyhow::{Context as _, anyhow};
use fvm_shared::crypto::signature::{
SECP_PUB_LEN, SECP_SIG_LEN, SECP_SIG_MESSAGE_HASH_SIZE, SignatureType,
};
use fvm_shared::piece::PieceInfo;
use fvm_shared::sector::{
AggregateSealVerifyProofAndInfos, RegisteredSealProof, ReplicaUpdateInfo, SealVerifyInfo,
WindowPoStVerifyInfo,
};
use fvm_shared::sys;
use num_traits::FromPrimitive;
use super::Context;
use crate::kernel::{ClassifyResult, Result};
use crate::{Kernel, syscall_error};
#[allow(clippy::too_many_arguments)]
pub fn verify_signature(
context: Context<'_, impl Kernel>,
sig_type: u32,
sig_off: u32,
sig_len: u32,
addr_off: u32,
addr_len: u32,
plaintext_off: u32,
plaintext_len: u32,
) -> Result<i32> {
let sig_type = SignatureType::from_u32(sig_type)
.with_context(|| format!("unknown signature type {}", sig_type))
.or_illegal_argument()?;
let sig_bytes = context.memory.try_slice(sig_off, sig_len)?;
let addr = context.memory.read_address(addr_off, addr_len)?;
let plaintext = context.memory.try_slice(plaintext_off, plaintext_len)?;
context
.kernel
.verify_signature(sig_type, sig_bytes, &addr, plaintext)
.map(|v| if v { 0 } else { -1 })
}
pub fn recover_secp_public_key(
context: Context<'_, impl Kernel>,
hash_off: u32,
sig_off: u32,
) -> Result<[u8; SECP_PUB_LEN]> {
let hash_bytes = context
.memory
.try_slice(hash_off, SECP_SIG_MESSAGE_HASH_SIZE as u32)?
.try_into()
.or_illegal_argument()?;
let sig_bytes = context
.memory
.try_slice(sig_off, SECP_SIG_LEN as u32)?
.try_into()
.or_illegal_argument()?;
context
.kernel
.recover_secp_public_key(&hash_bytes, &sig_bytes)
}
pub fn hash(
context: Context<'_, impl Kernel>,
hash_code: u64,
data_off: u32, data_len: u32,
digest_off: u32, digest_len: u32,
) -> Result<u32> {
context.memory.check_bounds(digest_off, digest_len)?;
let digest = {
let data = context.memory.try_slice(data_off, data_len)?;
context.kernel.hash(hash_code, data)?
};
let digest_out = context.memory.try_slice_mut(digest_off, digest_len)?;
let length = cmp::min(digest_out.len(), digest.digest().len());
digest_out[..length].copy_from_slice(&digest.digest()[..length]);
Ok(length as u32)
}
pub fn compute_unsealed_sector_cid(
context: Context<'_, impl Kernel>,
proof_type: i64, pieces_off: u32, pieces_len: u32,
cid_off: u32,
cid_len: u32,
) -> Result<u32> {
let typ = RegisteredSealProof::from(proof_type);
if let RegisteredSealProof::Invalid(invalid) = typ {
return Err(syscall_error!(IllegalArgument; "invalid proof type {}", invalid).into());
}
let pieces: Vec<PieceInfo> = context.memory.read_cbor(pieces_off, pieces_len)?;
context.memory.check_bounds(cid_off, cid_len)?;
let cid = context
.kernel
.compute_unsealed_sector_cid(typ, pieces.as_slice())?;
context.memory.write_cid(&cid, cid_off, cid_len)
}
pub fn verify_seal(
context: Context<'_, impl Kernel>,
info_off: u32, info_len: u32,
) -> Result<i32> {
let info = context
.memory
.read_cbor::<SealVerifyInfo>(info_off, info_len)?;
context
.kernel
.verify_seal(&info)
.map(|v| if v { 0 } else { -1 })
}
pub fn verify_post(
context: Context<'_, impl Kernel>,
info_off: u32, info_len: u32,
) -> Result<i32> {
let info = context
.memory
.read_cbor::<WindowPoStVerifyInfo>(info_off, info_len)?;
context
.kernel
.verify_post(&info)
.map(|v| if v { 0 } else { -1 })
}
pub fn verify_consensus_fault(
context: Context<'_, impl Kernel>,
h1_off: u32,
h1_len: u32,
h2_off: u32,
h2_len: u32,
extra_off: u32,
extra_len: u32,
) -> Result<sys::out::crypto::VerifyConsensusFault> {
let h1 = context.memory.try_slice(h1_off, h1_len)?;
let h2 = context.memory.try_slice(h2_off, h2_len)?;
let extra = context.memory.try_slice(extra_off, extra_len)?;
let ret = context.kernel.verify_consensus_fault(h1, h2, extra)?;
match ret {
Some(fault) => Ok(sys::out::crypto::VerifyConsensusFault {
fault: fault.fault_type as u32,
epoch: fault.epoch,
target: fault
.target
.id()
.context("kernel returned non-id target address")
.or_fatal()?,
}),
None => Ok(sys::out::crypto::VerifyConsensusFault {
fault: 0,
epoch: 0,
target: 0,
}),
}
}
pub fn verify_aggregate_seals(
context: Context<'_, impl Kernel>,
agg_off: u32, agg_len: u32,
) -> Result<i32> {
let info = context
.memory
.read_cbor::<AggregateSealVerifyProofAndInfos>(agg_off, agg_len)?;
context
.kernel
.verify_aggregate_seals(&info)
.map(|v| if v { 0 } else { -1 })
}
pub fn verify_replica_update(
context: Context<'_, impl Kernel>,
rep_off: u32, rep_len: u32,
) -> Result<i32> {
let info = context
.memory
.read_cbor::<ReplicaUpdateInfo>(rep_off, rep_len)?;
context
.kernel
.verify_replica_update(&info)
.map(|v| if v { 0 } else { -1 })
}
pub fn batch_verify_seals(
context: Context<'_, impl Kernel>,
batch_off: u32,
batch_len: u32,
result_off: u32,
) -> Result<()> {
let batch = context
.memory
.read_cbor::<Vec<SealVerifyInfo>>(batch_off, batch_len)?;
let output = context
.memory
.try_slice_mut(result_off, batch.len() as u32)?;
let result = context.kernel.batch_verify_seals(&batch)?;
if result.len() != batch.len() {
return Err(anyhow!(
"expected one result per input: {} != {}",
batch.len(),
result.len()
))
.or_fatal();
}
unsafe {
output.copy_from_slice(&*(&*result as *const [bool] as *const [u8]));
}
Ok(())
}