Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

slightly safer sig verification #1464

Merged
merged 4 commits into from
Feb 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- program: add posted slot tail to order struct, use it to determine vamm availability for high volume users ([#1459](https://github.com/drift-labs/protocol-v2/pull/1459))
- program: add pyth lazer stable coin oracle type ([#1463](https://github.com/drift-labs/protocol-v2/pull/1463))
- program: removes devnet panics for swift and slightly changes sig verification ([#1464](https://github.com/drift-labs/protocol-v2/pull/1464))
- program: allow hot wallet admin to init market if not active ([#1454](https://github.com/drift-labs/protocol-v2/pull/1454))
- program: round down 1 for calculate_max_withdrawable ([#1461](https://github.com/drift-labs/protocol-v2/pull/1461))
- program: add fuel overflow account ([#1449](https://github.com/drift-labs/protocol-v2/pull/1449))
Expand Down
7 changes: 1 addition & 6 deletions programs/drift/src/instructions/keeper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -660,11 +660,6 @@ pub fn place_swift_taker_order<'c: 'info, 'info>(
state: &State,
is_delegate_signer: bool,
) -> Result<()> {
#[cfg(all(feature = "mainnet-beta", not(feature = "anchor-test")))]
{
panic!("Swift orders are disabled on mainnet-beta");
}

// Authenticate the swift param message
let ix_idx = load_current_index_checked(ix_sysvar)?;
validate!(
Expand All @@ -682,10 +677,10 @@ pub fn place_swift_taker_order<'c: 'info, 'info>(
};
let verified_message_and_signature = verify_ed25519_msg(
&ix,
ix_sysvar,
ix_idx,
&signer,
&taker_order_params_message_bytes[..],
12,
)?;

let taker_order_params_message: SwiftOrderParamsMessage =
Expand Down
5 changes: 0 additions & 5 deletions programs/drift/src/instructions/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,11 +296,6 @@ pub fn handle_initialize_swift_user_orders<'c: 'info, 'info>(
ctx: Context<'_, '_, 'c, 'info, InitializeSwiftUserOrders<'info>>,
num_orders: u16,
) -> Result<()> {
#[cfg(all(feature = "mainnet-beta", not(feature = "anchor-test")))]
{
panic!("Swift orders are disabled on mainnet-beta");
}

let swift_user_orders = &mut ctx.accounts.swift_user_orders;
swift_user_orders.authority_pubkey = ctx.accounts.authority.key();
swift_user_orders
Expand Down
65 changes: 54 additions & 11 deletions programs/drift/src/validation/sig_verification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use byteorder::ByteOrder;
use byteorder::LE;
use solana_program::ed25519_program::ID as ED25519_ID;
use solana_program::instruction::Instruction;
use solana_program::program_memory::sol_memcmp;
use solana_program::sysvar;
use std::convert::TryInto;

const ED25519_PROGRAM_INPUT_HEADER_LEN: usize = 2;
Expand Down Expand Up @@ -118,6 +120,10 @@ pub struct VerifiedMessage {
pub signature: [u8; 64],
}

fn slice_eq(a: &[u8], b: &[u8]) -> bool {
a.len() == b.len() && sol_memcmp(a, b, a.len()) == 0
}

/// Check Ed25519Program instruction data verifies the given msg
///
/// `ix` an Ed25519Program instruction [see](https://github.com/solana-labs/solana/blob/master/sdk/src/ed25519_instruction.rs))
Expand All @@ -127,36 +133,50 @@ pub struct VerifiedMessage {
/// `pubkey` expected pubkey of the signer
///
pub fn verify_ed25519_msg(
ix: &Instruction,
ed25519_ix: &Instruction,
instructions_sysvar: &AccountInfo,
current_ix_index: u16,
signer: &[u8; 32],
msg: &[u8],
message_offset: u16,
) -> Result<VerifiedMessage> {
if ix.program_id != ED25519_ID || ix.accounts.len() != 0 {
msg!("Invalid Ix: program ID: {:?}", ix.program_id);
msg!("Invalid Ix: accounts: {:?}", ix.accounts.len());
if ed25519_ix.program_id != ED25519_ID || ed25519_ix.accounts.len() != 0 {
msg!("Invalid Ix: program ID: {:?}", ed25519_ix.program_id);
msg!("Invalid Ix: accounts: {:?}", ed25519_ix.accounts.len());
return Err(ErrorCode::SigVerificationFailed.into());
}

let ix_data = &ix.data;
let ed25519_ix_data = &ed25519_ix.data;
// According to this layout used by the Ed25519Program]
if ix_data.len() < 2 {
if ed25519_ix_data.len() < 2 {
msg!(
"Invalid Ix, should be header len = 2. data: {:?}",
ix.data.len(),
ed25519_ix.data.len(),
);
return Err(SignatureVerificationError::InvalidEd25519InstructionDataLength.into());
}

// Parse the ix data into the offsets
let num_signatures = ed25519_ix_data[0];
let signature_index = 0;
if signature_index >= num_signatures {
return Err(SignatureVerificationError::InvalidSignatureIndex.into());
}
let args: &[Ed25519SignatureOffsets] =
try_cast_slice(&ix_data[ED25519_PROGRAM_INPUT_HEADER_LEN..]).map_err(|_| {
try_cast_slice(&ed25519_ix_data[ED25519_PROGRAM_INPUT_HEADER_LEN..]).map_err(|_| {
msg!("Invalid Ix: failed to cast slice");
ErrorCode::SigVerificationFailed
})?;

let args_len = args
.len()
.try_into()
.map_err(|_| SignatureVerificationError::InvalidEd25519InstructionDataLength)?;
if signature_index >= args_len {
return Err(SignatureVerificationError::InvalidSignatureIndex.into());
}

let offsets = &args[0];
let message_offset = offsets.signature_offset;
if offsets.signature_offset != message_offset {
msg!(
"Invalid Ix: signature offset: {:?}",
Expand All @@ -165,9 +185,28 @@ pub fn verify_ed25519_msg(
return Err(ErrorCode::SigVerificationFailed.into());
}

let expected_public_key_offset = message_offset
let self_instruction = sysvar::instructions::load_instruction_at_checked(
current_ix_index.into(),
instructions_sysvar,
)
.map_err(|_| SignatureVerificationError::LoadInstructionAtFailed)?;

let message_end_offset = offsets
.message_data_offset
.checked_add(offsets.message_data_size)
.ok_or(SignatureVerificationError::MessageOffsetOverflow)?;
let expected_message_data = self_instruction
.data
.get(usize::from(message_offset)..usize::from(message_end_offset))
.ok_or(SignatureVerificationError::InvalidMessageOffset)?;
if !slice_eq(expected_message_data, msg) {
return Err(SignatureVerificationError::InvalidMessageData.into());
}

let expected_public_key_offset = offsets
.signature_offset
.checked_add(SIGNATURE_LEN)
.ok_or(ErrorCode::SigVerificationFailed)?;
.ok_or(SignatureVerificationError::MessageOffsetOverflow)?;
if offsets.public_key_offset != expected_public_key_offset {
msg!(
"Invalid Ix: public key offset: {:?}, expected: {:?}",
Expand Down Expand Up @@ -287,4 +326,8 @@ pub enum SignatureVerificationError {
MessageOffsetOverflow,
#[msg("invalid message hex")]
InvalidMessageHex,
#[msg("invalid message data")]
InvalidMessageData,
#[msg("loading custom ix at index failed")]
LoadInstructionAtFailed,
}
6 changes: 6 additions & 0 deletions sdk/src/idl/drift.json
Original file line number Diff line number Diff line change
Expand Up @@ -12148,6 +12148,12 @@
},
{
"name": "InvalidMessageHex"
},
{
"name": "InvalidMessageData"
},
{
"name": "LoadInstructionAtFailed"
}
]
}
Expand Down
Loading