Skip to content

Commit

Permalink
feat: add authenticate message to account actor
Browse files Browse the repository at this point in the history
  • Loading branch information
arajasek committed Aug 3, 2022
1 parent 95069eb commit 23b7dd7
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 2 deletions.
47 changes: 45 additions & 2 deletions actors/account/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,24 @@
use fvm_ipld_blockstore::Blockstore;
use fvm_ipld_encoding::RawBytes;
use fvm_shared::address::{Address, Protocol};
use fvm_shared::crypto::signature::SignatureType::{Secp256k1, BLS};
use fvm_shared::crypto::signature::{Signature, SignatureType};
use fvm_shared::error::ExitCode;
use fvm_shared::{MethodNum, METHOD_CONSTRUCTOR};
use num_derive::FromPrimitive;
use num_traits::FromPrimitive;

use crate::types::AuthenticateMessageParams;
use fil_actors_runtime::builtin::singletons::SYSTEM_ACTOR_ADDR;
use fil_actors_runtime::cbor;
use fil_actors_runtime::runtime::{ActorCode, Runtime};
use fil_actors_runtime::{actor_error, ActorError};
use fil_actors_runtime::{cbor, ActorDowncast};

pub use self::state::State;

mod state;
pub mod testing;
pub mod types;

#[cfg(feature = "fil-actor")]
fil_actors_runtime::wasm_trampoline!(Actor);
Expand All @@ -29,10 +34,12 @@ fil_actors_runtime::wasm_trampoline!(Actor);
pub enum Method {
Constructor = METHOD_CONSTRUCTOR,
PubkeyAddress = 2,
AuthenticateMessage = 3,
}

/// Account Actor
pub struct Actor;

impl Actor {
/// Constructor for Account actor
pub fn constructor<BS, RT>(rt: &mut RT, address: Address) -> Result<(), ActorError>
Expand All @@ -52,7 +59,7 @@ impl Actor {
Ok(())
}

// Fetches the pubkey-type address from this actor.
/// Fetches the pubkey-type address from this actor.
pub fn pubkey_address<BS, RT>(rt: &mut RT) -> Result<Address, ActorError>
where
BS: Blockstore,
Expand All @@ -62,6 +69,38 @@ impl Actor {
let st: State = rt.state()?;
Ok(st.address)
}

/// Authenticates whether the provided signature is valid for the provided message.
/// Errors if the authentication is invalid.
pub fn authenticate_message<BS, RT>(
rt: &mut RT,
params: AuthenticateMessageParams,
) -> Result<(), ActorError>
where
BS: Blockstore,
RT: Runtime<BS>,
{
rt.validate_immediate_caller_accept_any()?;
let st: State = rt.state()?;
let address = st.address;
let sig_type: SignatureType = match address.protocol() {
Protocol::Secp256k1 => Secp256k1,
Protocol::BLS => BLS,
protocol => {
return Err(actor_error!(illegal_state;
"account address must use BLS or SECP protocol, got {}", protocol));
}
};
let sig = Signature { sig_type, bytes: params.signature };
rt.verify_signature(&sig, &st.address, &params.message).map_err(|e| {
e.downcast_default(
ExitCode::USR_ILLEGAL_ARGUMENT,
"failed to authenticate message, signature invalid",
)
})?;

Ok(())
}
}

impl ActorCode for Actor {
Expand All @@ -83,6 +122,10 @@ impl ActorCode for Actor {
let addr = Self::pubkey_address(rt)?;
Ok(RawBytes::serialize(addr)?)
}
Some(Method::AuthenticateMessage) => {
Self::authenticate_message(rt, cbor::deserialize_params(params)?)?;
Ok(RawBytes::default())
}
None => Err(actor_error!(unhandled_message; "Invalid method")),
}
}
Expand Down
15 changes: 15 additions & 0 deletions actors/account/src/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright 2019-2022 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

use fvm_ipld_encoding::tuple::*;
use fvm_ipld_encoding::{serde_bytes, Cbor};

#[derive(Debug, Serialize_tuple, Deserialize_tuple)]
pub struct AuthenticateMessageParams {
#[serde(with = "serde_bytes")]
pub signature: Vec<u8>,
#[serde(with = "serde_bytes")]
pub message: Vec<u8>,
}

impl Cbor for AuthenticateMessageParams {}
26 changes: 26 additions & 0 deletions test_vm/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -657,3 +657,29 @@ pub mod invariant_failure_patterns {
Regex::new("^reward state epoch \\d+ does not match prior_epoch\\+1 \\d+$").unwrap();
}
}

pub fn generate_deal_proposal(
client: Address,
provider: Address,
client_collateral: TokenAmount,
provider_collateral: TokenAmount,
start_epoch: ChainEpoch,
end_epoch: ChainEpoch,
) -> DealProposal {
let piece_cid = make_piece_cid("1".as_bytes());
let piece_size = PaddedPieceSize(2048u64);
let storage_per_epoch = BigInt::from(10u8);
DealProposal {
piece_cid,
piece_size,
verified_deal: false,
client,
provider,
label: Label::String("label".to_string()),
start_epoch,
end_epoch,
storage_price_per_epoch: storage_per_epoch,
provider_collateral,
client_collateral,
}
}
53 changes: 53 additions & 0 deletions test_vm/tests/account_authenticate_message_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use fil_actor_account::types::AuthenticateMessageParams;
use fil_actor_account::Method::AuthenticateMessage;
use fvm_ipld_blockstore::MemoryBlockstore;
use fvm_ipld_encoding::RawBytes;
use fvm_shared::bigint::Zero;
use fvm_shared::econ::TokenAmount;
use fvm_shared::error::ExitCode;
use test_vm::util::{apply_code, apply_ok, create_accounts, generate_deal_proposal};
use test_vm::{TEST_VM_INVALID, VM};

// Using a deal proposal as a serialized message, we confirm that:
// - calls to authenticate_message with valid signatures succeed
// - calls to authenticate_message with invalid signatures fail
#[test]
fn account_authenticate_message() {
let store = MemoryBlockstore::new();
let v = VM::new_with_singletons(&store);
let addr = create_accounts(&v, 1, TokenAmount::from(10_000e18 as i128))[0];

let proposal =
generate_deal_proposal(addr, addr, TokenAmount::zero(), TokenAmount::zero(), 0, 0);
let proposal_ser =
RawBytes::serialize(proposal).expect("failed to marshal deal proposal").to_vec();

// With a good sig, message succeeds
let authenticate_message_params = AuthenticateMessageParams {
signature: proposal_ser.clone(),
message: proposal_ser.clone(),
};
apply_ok(
&v,
addr,
addr,
TokenAmount::zero(),
AuthenticateMessage as u64,
authenticate_message_params,
);

// Bad, bad sig! message fails
let authenticate_message_params = AuthenticateMessageParams {
signature: TEST_VM_INVALID.as_bytes().to_vec(),
message: proposal_ser,
};
apply_code(
&v,
addr,
addr,
TokenAmount::zero(),
AuthenticateMessage as u64,
authenticate_message_params,
ExitCode::USR_ILLEGAL_ARGUMENT,
);
}

0 comments on commit 23b7dd7

Please sign in to comment.