Skip to content
Draft
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
30 changes: 26 additions & 4 deletions key-wallet-ffi/src/account_derivation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,26 @@ use std::ptr;

// No extra FFI enum for chain selection; account semantics decide path.

fn validate_bls_seed_len(seed_len: usize, error: *mut FFIError) -> bool {
if !(16..=64).contains(&seed_len) {
unsafe {
(*error).set(FFIErrorCode::InvalidInput, "Seed length must be between 16 and 64 bytes");
}
return false;
}
true
}

fn validate_eddsa_seed_len(seed_len: usize, error: *mut FFIError) -> bool {
if seed_len < 16 {
unsafe {
(*error).set(FFIErrorCode::InvalidInput, "Seed length must be at least 16 bytes");
}
return false;
}
true
}

/// Derive an extended private key from an account at a given index, using the provided master xpriv.
///
/// Returns an opaque FFIExtendedPrivKey pointer that must be freed with `extended_private_key_free`.
Expand Down Expand Up @@ -65,7 +85,7 @@ pub unsafe extern "C" fn account_derive_extended_private_key_at(
///
/// # Safety
/// - `account` must be a valid, non-null pointer to an `FFIBLSAccount` (only when `bls` feature is enabled).
/// - `seed` must point to a readable buffer of length `seed_len` (1..=64 bytes expected).
/// - `seed` must point to a readable buffer of length `seed_len` (16..=64 bytes expected).
/// - `error` must be a valid pointer to an FFIError.
/// - Returned string must be freed with `string_free`.
#[cfg(feature = "bls")]
Expand All @@ -79,8 +99,7 @@ pub unsafe extern "C" fn bls_account_derive_private_key_from_seed(
) -> *mut c_char {
let account = deref_ptr!(account, error);
check_ptr!(seed, error);
if seed_len == 0 || seed_len > 64 {
(*error).set(FFIErrorCode::InvalidInput, "Seed length must be between 1 and 64 bytes");
if !validate_bls_seed_len(seed_len, error) {
return ptr::null_mut();
}
let seed_slice = std::slice::from_raw_parts(seed, seed_len);
Expand Down Expand Up @@ -147,7 +166,7 @@ pub unsafe extern "C" fn bls_account_derive_private_key_from_mnemonic(
///
/// # Safety
/// - `account` must be a valid, non-null pointer to an `FFIEdDSAAccount` (only when `eddsa` feature is enabled).
/// - `seed` must point to a readable buffer of length `seed_len` (1..=64 bytes expected).
/// - `seed` must point to a readable buffer of length `seed_len` (at least 16 bytes expected).
/// - `error` must be a valid pointer to an FFIError.
/// - Returned string must be freed with `string_free`.
#[cfg(feature = "eddsa")]
Expand All @@ -161,6 +180,9 @@ pub unsafe extern "C" fn eddsa_account_derive_private_key_from_seed(
) -> *mut c_char {
let account = deref_ptr!(account, error);
check_ptr!(seed, error);
if !validate_eddsa_seed_len(seed_len, error) {
return ptr::null_mut();
}
let seed_slice = std::slice::from_raw_parts(seed, seed_len);
let sk = unwrap_or_return!(
account.inner().derive_from_seed_private_key_at(seed_slice, index),
Expand Down
73 changes: 71 additions & 2 deletions key-wallet-ffi/src/account_derivation_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ mod tests {
use crate::types::FFIAccountKind;
use crate::wallet;
use dash_network::ffi::FFINetwork;
#[cfg(feature = "bls")]
use key_wallet::account::{AccountType, BLSAccount};

const MNEMONIC: &str =
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
const MNEMONIC: &str = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";

#[test]
fn test_account_derive_private_key_at_receive_index() {
Expand Down Expand Up @@ -246,4 +247,72 @@ mod tests {
wallet::wallet_free(wallet);
}
}

#[cfg(feature = "bls")]
#[test]
fn test_bls_seed_helper_rejects_invalid_seed_lengths() {
let mut error = FFIError::default();
let account = BLSAccount::from_seed(
None,
AccountType::ProviderOperatorKeys,
[1u8; 32],
dashcore::Network::Testnet,
)
.unwrap();
let ffi_account = crate::account::FFIBLSAccount::new(&account);
let short_seed = [0u8; 15];
let long_seed = [0u8; 65];

let too_short = unsafe {
bls_account_derive_private_key_from_seed(
&ffi_account,
short_seed.as_ptr(),
short_seed.len(),
0,
&mut error,
)
};
assert!(too_short.is_null());
assert_eq!(error.code, FFIErrorCode::InvalidInput);

error = FFIError::default();
let too_long = unsafe {
bls_account_derive_private_key_from_seed(
&ffi_account,
long_seed.as_ptr(),
long_seed.len(),
0,
&mut error,
)
};
assert!(too_long.is_null());
assert_eq!(error.code, FFIErrorCode::InvalidInput);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

#[cfg(feature = "eddsa")]
#[test]
fn test_eddsa_seed_helper_rejects_invalid_seed_lengths() {
let mut error = FFIError::default();
let account = key_wallet::account::EdDSAAccount::from_seed(
None,
FFIAccountKind::ProviderPlatformKeys.to_account_type(0),
[1u8; 32],
FFINetwork::Testnet.into(),
)
.unwrap();
let ffi_account = crate::account::FFIEdDSAAccount::new(&account);
let short_seed = [0u8; 15];

let too_short = unsafe {
eddsa_account_derive_private_key_from_seed(
&ffi_account,
short_seed.as_ptr(),
short_seed.len(),
0,
&mut error,
)
};
assert!(too_short.is_null());
assert_eq!(error.code, FFIErrorCode::InvalidInput);
}
}
Loading