Message Authentication
In the Intent engine, message authentication is a critical part of ensuring that flows are authorized and executed by the correct entities. This document explains the authentication process for various message types, with a focus on local messages, MsgExec messages, and ICA messages.
Authentication Overview
The module processes three primary message types:
Local Messages:
- These are standard messages sent directly by users.
- Authentication ensures that the signer matches the flow owner specified in the request.
Authz
MsgExecMessages:- These messages allow one account to execute messages on behalf of another.
- Authentication involves verifying the signers for all contained messages to ensure proper delegation.
Proxy Account Messages:
- These are messages sent via IBC (Inter-Blockchain Communication) through a Proxy Account (ICA or Union Proxy).
- Trustless Agent messages can only call
MsgExecfor security reasons (requiring AuthZ grants on the host), while Self-Hosted Proxy messages are allowed without restrictions (as the user controls the account).
Authentication Logic
Local Messages
Local messages require a direct match between the signer and the flow owner. The module validates this relationship explicitly:
if isLocalMessage(flow) {
return k.validateSigners(ctx, codec, flow, message)
}
Authz MsgExec Messages
For MsgExec messages, authentication is applied to each inner message:
if isAuthzMsgExec(message) {
return k.validateAuthzMsg(ctx, codec, flow, message)
}
This ensures that delegation rules are respected and all flows performed on behalf of another account are authorized.
Trustless Agent Messages
Trustless Agent messages are authenticated differently. Since the proxy execution happens via IBC (either ICA packet or Union ZK proof), the packet source is authenticated by the transport layer. Our module controls which Trustless Agent to use through the configuration provided by the user.
For Trustless Agents (Cosmos), messages are restricted to MsgExec only. This ensures the agent can only perform actions for which the user has explicitly granted authorization (via AuthZ) on the host chain.
if isTrustlessAgentMessage(flow) {
if message.TypeUrl != sdk.MsgTypeURL(&authztypes.MsgExec{}) {
return errorsmod.Wrap(sdkerrors.ErrUnauthorized, "only MsgExec is allowed for Trustless Agent messages")
}
return nil
}
Self-Hosted Proxy Messages
Self-Hosted Proxy messages (e.g., standard ICA or Union EVM execution) do not require additional restrictions from the Intent Engine's perspective. They are trusted based on the ownership of the flow.
if isSelfHostedICAMessage(flow) {
return nil
}
Validation
ICA Authentication:
- IBC ensures that the packet sender is authenticated via AuthenticateTx as part of its protocol.
- This removes the need for additional signer checks within the Intent engine.
Controlled Configuration:
- The flow submission from the user specifies which Trustless Execution Agent (and the fee configuration thereof) is used, and this configuration is already expected and verified during setup.
Security for
MsgExec:- Restricting Trustless Execution Agent messages to
MsgExecensures that only delegated flows are performed, maintaining the security model.
- Restricting Trustless Execution Agent messages to
Example Code: validateMessage
The following code demonstrates how the module handles authentication for different message types:
func (k Keeper) validateMessage(ctx sdk.Context, codec codec.Codec, flow types.Flow, message *codectypes.Any) error {
var sdkMsg sdk.Msg
if err := codec.UnpackAny(message, &sdkMsg); err != nil {
return errorsmod.Wrap(err, "failed to unpack message")
}
switch {
case isAuthzMsgExec(message):
// Validate Authz MsgExec messages.
return k.validateAuthzMsg(ctx, codec, flow, message)
case isLocalMessage(flow):
// Validate local messages to ensure the signer matches the owner.
return k.validateSigners(ctx, codec, flow, message)
case isTrustlessAgentMessage(flow):
// Restrict Trustless Agent messages to MsgExec for security.
if message.TypeUrl != sdk.MsgTypeURL(&authztypes.MsgExec{}) {
return errorsmod.Wrap(sdkerrors.ErrUnauthorized, "only MsgExec is allowed for Trustless Agent messages")
}
return nil
case isSelfHostedICAMessage(flow):
// Allow Self-hosted ICA messages without additional validation.
return nil
default:
// Unsupported message type.
return errorsmod.Wrap(sdkerrors.ErrUnauthorized, "unsupported message type")
}
}
Conclusion
Message authentication is handled carefully within the Intent engine to ensure security and correctness. While local and MsgExec messages require explicit validation, ICA messages rely on IBC’s inherent authentication mechanisms and user-controlled configurations. Restricting Trustless Execution Agent messages to MsgExec adds an additional layer of security, while Self-hosted ICAs are trusted based on owner control. This approach balances security with efficiency, adhering to the principles of our module’s design.