From b968c96783f20641ed62273bb697e1578e0d21b1 Mon Sep 17 00:00:00 2001 From: Nour Alharithi Date: Wed, 5 Feb 2025 11:06:09 -0800 Subject: [PATCH 1/3] slightly safer sig verification --- programs/drift/src/instructions/keeper.rs | 7 +- programs/drift/src/instructions/user.rs | 5 -- .../drift/src/validation/sig_verification.rs | 66 +++++++++++++++---- sdk/src/idl/drift.json | 6 ++ 4 files changed, 62 insertions(+), 22 deletions(-) diff --git a/programs/drift/src/instructions/keeper.rs b/programs/drift/src/instructions/keeper.rs index d8e8f908b..7ecd1ada7 100644 --- a/programs/drift/src/instructions/keeper.rs +++ b/programs/drift/src/instructions/keeper.rs @@ -657,11 +657,6 @@ pub fn place_swift_taker_order<'c: 'info, 'info>( oracle_map: &mut OracleMap, state: &State, ) -> 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!( @@ -674,10 +669,10 @@ pub fn place_swift_taker_order<'c: 'info, 'info>( let ix: Instruction = load_instruction_at_checked(ix_idx as usize - 1, ix_sysvar)?; let verified_message_and_signature = verify_ed25519_msg( &ix, + ix_sysvar, ix_idx, &taker.authority.to_bytes(), &taker_order_params_message_bytes[..], - 12, )?; let taker_order_params_message: SwiftOrderParamsMessage = diff --git a/programs/drift/src/instructions/user.rs b/programs/drift/src/instructions/user.rs index e1006cdd0..348523e46 100644 --- a/programs/drift/src/instructions/user.rs +++ b/programs/drift/src/instructions/user.rs @@ -292,11 +292,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 diff --git a/programs/drift/src/validation/sig_verification.rs b/programs/drift/src/validation/sig_verification.rs index a41133f85..e8646060c 100644 --- a/programs/drift/src/validation/sig_verification.rs +++ b/programs/drift/src/validation/sig_verification.rs @@ -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; @@ -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)) @@ -127,35 +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 { - 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[usize::from(signature_index)]; + + let message_offset = offsets.signature_offset; let offsets = &args[0]; if offsets.signature_offset != message_offset { msg!( @@ -165,9 +186,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: {:?}", @@ -287,4 +327,8 @@ pub enum SignatureVerificationError { MessageOffsetOverflow, #[msg("invalid message hex")] InvalidMessageHex, + #[msg("invalid message data")] + InvalidMessageData, + #[msg("loading custom ix at index failed")] + LoadInstructionAtFailed, } diff --git a/sdk/src/idl/drift.json b/sdk/src/idl/drift.json index 1fee39ab9..e15a2aee5 100644 --- a/sdk/src/idl/drift.json +++ b/sdk/src/idl/drift.json @@ -11982,6 +11982,12 @@ }, { "name": "InvalidMessageHex" + }, + { + "name": "InvalidMessageData" + }, + { + "name": "LoadInstructionAtFailed" } ] } From 875211c60e09d591fafc0eb0359d8f4bc922c60e Mon Sep 17 00:00:00 2001 From: Nour Alharithi Date: Wed, 5 Feb 2025 11:09:35 -0800 Subject: [PATCH 2/3] change log --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b34890f0b..2facd37fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,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)) ### Fixes From b7d5dcfaf133f629bdecc4e1d143135a71001351 Mon Sep 17 00:00:00 2001 From: Nour Alharithi Date: Wed, 12 Feb 2025 12:27:22 -0800 Subject: [PATCH 3/3] sig verification cleanup --- programs/drift/src/validation/sig_verification.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/programs/drift/src/validation/sig_verification.rs b/programs/drift/src/validation/sig_verification.rs index e8646060c..cfd3b8861 100644 --- a/programs/drift/src/validation/sig_verification.rs +++ b/programs/drift/src/validation/sig_verification.rs @@ -174,10 +174,9 @@ pub fn verify_ed25519_msg( if signature_index >= args_len { return Err(SignatureVerificationError::InvalidSignatureIndex.into()); } - let offsets = &args[usize::from(signature_index)]; - let message_offset = offsets.signature_offset; let offsets = &args[0]; + let message_offset = offsets.signature_offset; if offsets.signature_offset != message_offset { msg!( "Invalid Ix: signature offset: {:?}",