Skip to content

Commit

Permalink
logic: improve test coverage and error messages of ed25519_verify
Browse files Browse the repository at this point in the history
  • Loading branch information
blasrodri committed Oct 11, 2022
1 parent 7d9c73c commit bd8d375
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 55 deletions.
21 changes: 18 additions & 3 deletions runtime/near-vm-logic/src/logic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1137,6 +1137,13 @@ impl<'a> VMLogic<'a> {
///
/// `input_cost(num_bytes_signature) + input_cost(num_bytes_message) + input_cost(num_bytes_public_key) +
/// ed25519_verify_base + ed25519_verify_byte * num_bytes_message`
///
/// # Error
///
/// If the public key's size is not equal to 32 returns [HostError::Ed25519VerifyInvalidInput].
/// Also if the signature size is not equal to 64 or the signature's "s" scalar isn't properly reduced
/// then returns [HostError::Ed25519VerifyInvalidInput].
#[cfg(feature = "protocol_feature_ed25519_verify")]
pub fn ed25519_verify(
&mut self,
Expand All @@ -1148,15 +1155,23 @@ impl<'a> VMLogic<'a> {
pub_key_ptr: u64,
) -> Result<u64> {
use ed25519_dalek::{PublicKey, Signature, Verifier, SIGNATURE_LENGTH};
const MESSAGE_LENGTH: usize = 32;

self.gas_counter.pay_base(ed25519_verify_base)?;
if sig_len != SIGNATURE_LENGTH as u64 {
let msg = self.get_vec_from_memory_or_register(msg_ptr, msg_len)?;
dbg!(&msg);
if msg_len != MESSAGE_LENGTH as u64 || msg.len() != MESSAGE_LENGTH {
return Err(VMLogicError::HostError(HostError::Ed25519VerifyInvalidInput {
msg: "invalid signature length".to_string(),
msg: "invalid message length".to_string(),
}));
}
let msg = self.get_vec_from_memory_or_register(msg_ptr, msg_len)?;
let signature_array = self.get_vec_from_memory_or_register(sig_ptr, sig_len)?;
if sig_len != SIGNATURE_LENGTH as u64 || signature_array.len() != SIGNATURE_LENGTH {
return Err(VMLogicError::HostError(HostError::Ed25519VerifyInvalidInput {
msg: "invalid signature length".to_string(),
}));
}

let signature = Signature::from_bytes(&signature_array).map_err(|e| {
VMLogicError::HostError(HostError::Ed25519VerifyInvalidInput { msg: e.to_string() })
})?;
Expand Down
146 changes: 94 additions & 52 deletions runtime/near-vm-logic/src/tests/miscs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -876,6 +876,11 @@ fn test_ed25519_verify() {

let bad_signature: [u8; 64] = [1; 64];

let mut forged_signature = signature.clone();
// create a forged signature with the `s` scalar not properly reduced
// https://docs.rs/ed25519/latest/src/ed25519/lib.rs.html#302
forged_signature[63] = 0b1110_0001;

let public_key: [u8; 32] = [
32, 122, 6, 120, 146, 130, 30, 37, 215, 112, 241, 251, 160, 196, 124, 17, 255, 75, 129, 62,
84, 22, 46, 206, 158, 184, 57, 224, 118, 35, 26, 182,
Expand All @@ -887,59 +892,96 @@ fn test_ed25519_verify() {
107, 108, 97, 100, 106, 102, 107, 108, 106, 97, 100, 115, 107,
];

let result = logic
.ed25519_verify(
signature.len() as _,
signature.as_ptr() as _,
message.len() as _,
message.as_ptr() as _,
public_key.len() as _,
public_key.as_ptr() as _,
)
.unwrap();

assert_eq!(result, 1);

assert_costs(map! {
ExtCosts::read_memory_byte: 128,
ExtCosts::read_memory_base: 3,
ExtCosts::ed25519_verify_base: 1,
ExtCosts::ed25519_verify_byte: 32,
});
let wrong_message_zero_bytes: [u8; 0] = [];

let scenarios = [
(
signature.len(),
signature.clone(),
message.len(),
message.as_slice(),
public_key.len(),
public_key.clone(),
Ok(1),
),
(
bad_signature.len(),
bad_signature.clone(),
message.len(),
message.as_slice(),
public_key.len(),
public_key.clone(),
Ok(0),
),
(
signature.len() - 1,
signature.clone(),
message.len(),
message.as_slice(),
public_key.len(),
public_key.clone(),
Err(VMLogicError::HostError(HostError::Ed25519VerifyInvalidInput {
msg: "invalid signature length".to_string(),
})),
),
(
forged_signature.len(),
forged_signature.clone(),
message.len(),
message.as_slice(),
public_key.len(),
public_key.clone(),
Err(VMLogicError::HostError(HostError::Ed25519VerifyInvalidInput {
msg: "signature error".to_string(),
})),
),
(
forged_signature.len(),
forged_signature.clone(),
0,
message.as_slice(),
public_key.len(),
public_key.clone(),
Err(VMLogicError::HostError(HostError::Ed25519VerifyInvalidInput {
msg: "invalid message length".to_string(),
})),
),
(
signature.len(),
signature.clone(),
message.len(),
wrong_message_zero_bytes.as_slice(), // note that here the message len is correct - TODO: ensure that this is the desired behavior
public_key.len(),
public_key.clone(),
Ok(0),
),
];

let result = logic
.ed25519_verify(
bad_signature.len() as _,
bad_signature.as_ptr() as _,
message.len() as _,
for (
signature_len,
signature,
message_len,
message,
public_key_len,
public_key,
expected_result,
) in scenarios
{
let result = logic.ed25519_verify(
signature_len as _,
signature.as_ptr() as _,
message_len as _,
message.as_ptr() as _,
public_key.len() as _,
public_key_len as _,
public_key.as_ptr() as _,
)
.unwrap();

assert_eq!(result, 0);

assert_costs(map! {
ExtCosts::read_memory_byte: 128,
ExtCosts::read_memory_base: 3,
ExtCosts::ed25519_verify_base: 1,
ExtCosts::ed25519_verify_byte: 32,
});

let result = logic.ed25519_verify(
(signature.len() - 1) as _,
signature.as_ptr() as _,
message.len() as _,
message.as_ptr() as _,
public_key.len() as _,
public_key.as_ptr() as _,
);

assert_eq!(
result,
Err(VMLogicError::HostError(HostError::Ed25519VerifyInvalidInput {
msg: "invalid signature length".to_string()
}))
);
);

assert_eq!(result, expected_result);
assert_costs(map! {
ExtCosts::read_memory_byte: 128,
ExtCosts::read_memory_base: 3,
ExtCosts::ed25519_verify_base: 1,
ExtCosts::ed25519_verify_byte: 32,
});
}
}

0 comments on commit bd8d375

Please sign in to comment.