Skip to content

Commit 694e4b2

Browse files
committed
Address review comments
1 parent 4735780 commit 694e4b2

File tree

5 files changed

+163
-18
lines changed

5 files changed

+163
-18
lines changed

Cargo.lock

Lines changed: 71 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,7 @@ wormhole-vaas-serde = "0.1.0"
3333
[dev-dependencies]
3434
serde_json = "1.0.140"
3535
base64 = "0.22.1"
36+
mockall = "0.13.1"
37+
38+
[profile.release]
39+
overflow-checks = true

src/main.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ const INVALID_UNRELIABLE_DATA_FORMAT: &str = "Invalid unreliable data format";
6161
const INVALID_PDA_MESSAGE: &str = "Invalid PDA message";
6262
const INVALID_EMITTER_CHAIN: &str = "Invalid emitter chain";
6363
const INVALID_ACCUMULATOR_ADDRESS: &str = "Invalid accumulator address";
64+
const INVALID_VAA_VERSION: &str = "Invalid VAA version";
6465

6566
fn decode_and_verify_update(
6667
wormhole_pid: &Pubkey,
@@ -87,6 +88,14 @@ fn decode_and_verify_update(
8788
anyhow::anyhow!(format!("{}: {}", INVALID_UNRELIABLE_DATA_FORMAT, e))
8889
})?;
8990

91+
if unreliable_data.message.vaa_version != 1 {
92+
tracing::error!(
93+
vaa_version = unreliable_data.message.vaa_version,
94+
"Unsupported VAA version"
95+
);
96+
return Err(anyhow::anyhow!(INVALID_VAA_VERSION));
97+
}
98+
9099
if Chain::Pythnet != unreliable_data.emitter_chain.into() {
91100
tracing::error!(
92101
emitter_chain = unreliable_data.emitter_chain,
@@ -320,7 +329,7 @@ mod tests {
320329
use secp256k1::SecretKey;
321330
use solana_account_decoder::{UiAccount, UiAccountData};
322331

323-
use crate::posted_message::MessageData;
332+
use crate::{posted_message::MessageData, signer::MockSigner};
324333

325334
fn get_wormhole_pid() -> Pubkey {
326335
Pubkey::from_str("H3fxXJ86ADW2PNuDDmZJg6mzTtPxkYCpNuQUTgmJ7AjU").unwrap()
@@ -530,4 +539,21 @@ mod tests {
530539
"30e41be3f10d3ac813f91e49e189bbb948d030be",
531540
);
532541
}
542+
543+
#[tokio::test]
544+
async fn test_sign_error() {
545+
let mut mock_signer = MockSigner::new();
546+
mock_signer
547+
.expect_sign()
548+
.return_once(|_| Err(anyhow::anyhow!("Mock signing error")));
549+
let unreliable_data = get_unreliable_data();
550+
let body = message_data_to_body(&unreliable_data);
551+
let err = Observation::try_new(body, Arc::new(mock_signer))
552+
.await
553+
.unwrap_err();
554+
assert_eq!(
555+
err.to_string(),
556+
"Failed to sign observation: Mock signing error"
557+
);
558+
}
533559
}

src/posted_message.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,4 +127,55 @@ mod tests {
127127
let decoded = PostedMessageUnreliableData::try_from_slice(encoded.as_slice()).unwrap();
128128
assert_eq!(decoded, post_message_unreliable_data);
129129
}
130+
131+
#[test]
132+
fn test_invalid_magic() {
133+
let post_message_unreliable_data = PostedMessageUnreliableData {
134+
message: MessageData {
135+
vaa_version: 1,
136+
consistency_level: 2,
137+
vaa_time: 3,
138+
vaa_signature_account: [4u8; 32],
139+
submission_time: 5,
140+
nonce: 6,
141+
sequence: 7,
142+
emitter_chain: 8,
143+
emitter_address: [9u8; 32],
144+
payload: vec![10u8; 32],
145+
},
146+
};
147+
148+
let mut encoded = borsh::to_vec(&post_message_unreliable_data).unwrap();
149+
encoded[0..3].copy_from_slice(b"foo"); // Invalid magic
150+
151+
let err = PostedMessageUnreliableData::try_from_slice(encoded.as_slice()).unwrap_err();
152+
assert_eq!(
153+
err.to_string(),
154+
"Magic mismatch. Expected [109, 115, 117] but got [102, 111, 111]"
155+
);
156+
}
157+
158+
#[test]
159+
fn test_invalid_data_length() {
160+
let post_message_unreliable_data = PostedMessageUnreliableData {
161+
message: MessageData {
162+
vaa_version: 1,
163+
consistency_level: 2,
164+
vaa_time: 3,
165+
vaa_signature_account: [4u8; 32],
166+
submission_time: 5,
167+
nonce: 6,
168+
sequence: 7,
169+
emitter_chain: 8,
170+
emitter_address: [9u8; 32],
171+
payload: vec![10u8; 32],
172+
},
173+
};
174+
175+
let mut encoded = borsh::to_vec(&post_message_unreliable_data).unwrap();
176+
encoded = encoded[0..encoded.len() - 1].to_vec();
177+
178+
let err = PostedMessageUnreliableData::try_from_slice(encoded.as_slice()).unwrap_err();
179+
assert_eq!(err.to_string(), "Unexpected length of input");
180+
}
130181
}

src/signer.rs

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use der::{
2-
asn1::{AnyRef, BitStringRef, UintRef},
2+
asn1::{AnyRef, BitStringRef},
33
oid::ObjectIdentifier,
44
Decode, Sequence,
55
};
@@ -13,12 +13,13 @@ use std::{
1313
use async_trait::async_trait;
1414
use prost::Message as ProstMessage;
1515
use secp256k1::{
16-
ecdsa::{RecoverableSignature, RecoveryId},
16+
ecdsa::{RecoverableSignature, RecoveryId, Signature},
1717
Message, PublicKey, Secp256k1, SecretKey,
1818
};
1919
use sequoia_openpgp::armor::{Kind, Reader, ReaderMode};
2020
use sha3::{Digest, Keccak256};
2121

22+
#[cfg_attr(test, mockall::automock)]
2223
#[async_trait]
2324
pub trait Signer: Send + Sync {
2425
async fn sign(&self, data: [u8; 32]) -> anyhow::Result<[u8; 65]>;
@@ -181,12 +182,6 @@ pub struct SubjectPublicKeyInfo<'a> {
181182
pub subject_public_key: BitStringRef<'a>,
182183
}
183184

184-
#[derive(Sequence)]
185-
struct EcdsaSignature<'a> {
186-
r: UintRef<'a>,
187-
s: UintRef<'a>,
188-
}
189-
190185
#[async_trait]
191186
impl Signer for KMSSigner {
192187
async fn sign(&self, data: [u8; 32]) -> anyhow::Result<[u8; 65]> {
@@ -204,14 +199,10 @@ impl Signer for KMSSigner {
204199
.signature
205200
.ok_or_else(|| anyhow::anyhow!("KMS did not return a signature"))?;
206201

207-
let decoded_signature = EcdsaSignature::from_der(kms_signature.as_ref())
208-
.map_err(|e| anyhow::anyhow!("Failed to decode SubjectPublicKeyInfo: {}", e))?;
209-
210-
let r_bytes = decoded_signature.r.as_bytes();
211-
let s_bytes = decoded_signature.s.as_bytes();
212-
let mut signature = [0u8; 65];
213-
signature[(32 - r_bytes.len())..32].copy_from_slice(r_bytes);
214-
signature[(64 - s_bytes.len())..64].copy_from_slice(decoded_signature.s.as_bytes());
202+
let mut signature = Signature::from_der(kms_signature.as_ref())
203+
.map_err(|e| anyhow::anyhow!("Failed to decode signature from KMS: {}", e))?;
204+
signature.normalize_s();
205+
let signature_bytes = signature.serialize_compact();
215206

216207
let public_key = self.get_public_key()?;
217208
for raw_id in 0..4 {
@@ -220,10 +211,12 @@ impl Signer for KMSSigner {
220211
.map_err(|e| anyhow::anyhow!("Failed to create RecoveryId: {}", e))?;
221212
if let Ok(recovered_public_key) = secp.recover_ecdsa(
222213
&Message::from_digest(data),
223-
&RecoverableSignature::from_compact(&signature[..64], recid)
214+
&RecoverableSignature::from_compact(&signature_bytes, recid)
224215
.map_err(|e| anyhow::anyhow!("Failed to create RecoverableSignature: {}", e))?,
225216
) {
226217
if recovered_public_key == public_key.0 {
218+
let mut signature = [0u8; 65];
219+
signature[..64].copy_from_slice(&signature_bytes);
227220
signature[64] = raw_id as u8;
228221
return Ok(signature);
229222
}

0 commit comments

Comments
 (0)