Skip to content

Conversation

@dnwiebe
Copy link
Collaborator

@dnwiebe dnwiebe commented Mar 22, 2025

No description provided.

masq_lib = { path = "../masq_lib" }
native-tls = "0.2.8"
node = { path = "../node", features = [ "expose_test_privates" ] }
node = { path = "../node" }
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed; Bert said it's obsolete

let stream_key = self.stream_key_factory.make();
let stream_key = self
.stream_key_factory
.make(self.main_cryptde.public_key(), server_addr);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. Maybe the StreamKeyFactory should carry around the Node's public key? It's not like it's going to change...

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to understand: Is the stream key used in the originating node to find the proper stream for a particular host? That means inboud_client_data.peer_addr is always the host IP we are asking for the web page or data?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already talked about this, but for posterity: no. InboundClientData.peer_addr (now called InboundClientData.client_addr) is the address associated with the stream from the browser (or other client) we're dealing with. Hashed with our public key, it ought to be universally unique.

"make_stream_key() inserted new key {} for {}", &stream_key, ibcd.peer_addr
"find_or_generate_stream_key() inserted new key {} for {}",
&stream_key,
ibcd.peer_addr
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another neglected name change

hash.update(uuid_bytes);
hash.update(public_key.as_ref());
hash = add_socket_addr_to_hash(hash, server_addr);
hash.update(STREAM_KEY_SALT.as_slice());
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the main change, right here; everything else supports it

hash.update(v4.ip().octets().as_slice());
}
SocketAddr::V6(v6) => {
hash.update(v6.ip().octets().as_slice());
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically, I don't think the Node officially supports IPv6 yet; but when it does, this will be one less thing to change.

}
}
let port = socket_addr.port();
hash.update(&[(port & 0xFF) as u8, (port >> 8) as u8]);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lower byte of the port, then upper byte of the port

fn stream_keys_from_different_public_keys_are_different() {
let server_addr = SocketAddr::new(IpAddr::from_str("1.2.3.4").unwrap(), 1024);

let stream_keys = vec![PublicKey::new(&[1, 2, 3]), PublicKey::new(&[1, 2, 2])]
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The public keys are only one bit different

let attack = StreamKey {
hash: hash.digest().bytes(),
};
assert_ne!(attack, result)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make a StreamKey without hashing in the salt, and it'll be wrong


#[test]
fn financials_command_retrieves_payable_and_receivable_records() {
fn financials_command_retrieves_payable_and_receivable_records_integration() {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because this test wasn't named xxx_integration, it wasn't being run. Since it wasn't being run, it didn't work. We fixed it.

assert_eq!(receivable[0].wallet, wallet_receivable_1.to_string());
assert_eq!(receivable[0].balance_gwei, 9000_i64);
assert_eq!(receivable[1].wallet, wallet_receivable_2.to_string());
assert_eq!(receivable[1].balance_gwei, 345678_i64);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't assert this way, because we can't depend on the order of the receivables. So we first have to figure out which order they're in, and then assert on them.

let data_dir = if sterile_database {
ensure_node_home_directory_exists("integration", test_name)
} else {
node_home_directory("integration", test_name)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the config_opt contains a --data-directory specification, it shouldn't be ignored like this.

Copy link
Collaborator

@czarte czarte left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we decided to add host_name and port to streamkeys

tiny-hderive = "0.3.0"
toml = "0.5.8"
websocket = {version = "0.26.2", default-features = false, features = ["sync"]}
rand = { version = "0.9.0", features = ["thread_rng"] }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

put this to alphabetical order please

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

let stream_key = self.stream_key_factory.make();
let stream_key = self
.stream_key_factory
.make(self.main_cryptde.public_key(), server_addr);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to understand: Is the stream key used in the originating node to find the proper stream for a particular host? That means inboud_client_data.peer_addr is always the host IP we are asking for the web page or data?

}

fn find_or_generate_stream_key(&mut self, ibcd: &InboundClientData) -> StreamKey {
fn find_or_generate_stream_key(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't see how we want to select the stream for particular host, as I understand, maybe wrongly, the peer_addr is something different of server_addr

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We talked about this, but for posterity: peer_addr (now called client_addr) is the address of the stream from the browser, not the stream to the server.

return Err("Browser request rejected due to missing consuming wallet".to_string());
}
let stream_key = proxy.find_or_generate_stream_key(&msg);
let stream_key = proxy.find_or_generate_stream_key(&msg, source_addr);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't source_addr be host_addr?

let uuid_bytes: &[u8] = uuid.as_bytes();
hash.update(uuid_bytes);
hash.update(public_key.as_ref());
hash = add_socket_addr_to_hash(hash, server_addr);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

server_addr is host_addr?

let ibcd_in = InboundClientData {
timestamp: SystemTime::now(),
peer_addr,
client_addr,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed for clarity

None => (Some(host.name), protocol_pack.standard_port()),
},
let (target_hostname_opt, target_port) = match target_host {
Some(host) => (Some(host.name), host.port),
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

host.port is no longer optional

last_data: ibcd.last_data,
},
target_hostname,
target_hostname: target_hostname_opt,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't correct the field name of ClientRequestPayload_0v1 without migrating to ClientRequestPayload_0v2, but I can correct the variable name...

match HttpProtocolPack::find_header_host(data.as_slice()) {
Some(host) => Some(host),
None => HttpProtocolPack::find_header_host(data.as_slice()),
None => HttpProtocolPack::find_url_host(data.as_slice()),
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Corrected the host-name priority

2 => Some(Host {
name: parts.remove(0).to_string(),
port: Self::port_from_string(parts.remove(0).to_string()),
port: HTTP_PORT,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now, instead of making a missing port number None here in HttpProtocolPack, where we know missing port numbers mean a default of 80, and then figuring out the number later when we run into the None outside the HTTP context, we're ditching the Option and defaulting it to 80 right here.

Err(_) => None,
Ok(port) => Some(port),
Err(_) => Err(format!("Port '{}' is not a number", port_str)),
Ok(port) => Ok(port),
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we can't parse a port number, that's not just "no port was specified;" that's an error that needs to be reported and logged somewhere.


assert_eq!(String::from("header.host.com"), host.name);
assert_eq!(host, Host::new("header.host.com", HTTP_PORT));
assert_eq!(HTTP_PORT, host.port);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that Host has a constructor, we can easily assert on the whole thing, rather than just one field.


#[test]
fn returns_host_name_and_port_from_url_if_both_exist() {
fn returns_host_name_and_port_from_header_if_both_exist() {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Priorities have been corrected.

let host = HttpProtocolPack {}.find_host(&data).unwrap();

assert_eq!(String::from("top.host.com"), host.name);
assert_eq!(host.name, String::from("top.host.com"));
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is old, old code, back from the days when Rust followed the JUnit convention of assert_eq!(expected, actual). Where I happened across these, I changed them to conform to the more modern assert_eq!(actual, expected) standard.

fn returns_host_name_from_url_when_no_scheme() {
let data = PlainData::new(
b"CONNECT good.url.dude/path.html HTTP/1.1\r\nHost: wrong.url.dude\r\n\r\n",
b"GET wrong.url.dude/path.html HTTP/1.1\r\nHost: good.url.dude\r\n\r\n",
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CONNECTs are unusual in the way their URL parameters are formed. The code under test doesn't care about that, but I still don't like a test with a manifestly incorrect request syntax, if I can avoid it. And I can. So I did.

}

#[test]
fn from_integration_test() {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant test; removed.

let msg_from_dispatcher = InboundClientData {
timestamp: SystemTime::now(),
peer_addr: socket_addr.clone(),
client_addr: socket_addr,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SocketAddr is Copy: no need for .clone().

Encrypted with 0x03040506: LiveHop { public_key: 0x, payer: Some(Payer { wallet: Wallet { kind: Address(0x71d0fc7d1c570b1ed786382b551a09391c91e33d) }, proof: Signature { v: 1, r: "9ca23557adf96d7aed407a06ce96851a4184e947a7b29b6c3872eef902fcba1e", s: "9ca23557adf96d7aed407a06ce96851a4184e947a7b29b6c3872eef902fcba1e" } }), component: Neighborhood }
Encrypted with 0x01020304: LiveHop { public_key: 0x02030405, payer: Some(Payer { wallet: Wallet { kind: Address(0x71d0fc7d1c570b1ed786382b551a09391c91e33d) }, proof: Signature { v: 0, r: "3e3a92d7284c2c2ff7119e9f7a7e183b062a335a598e965a47c36a2f288b6f8d", s: "3e3a92d7284c2c2ff7119e9f7a7e183b062a335a598e965a47c36a2f288b6f8d" } }), component: Hopper }
Encrypted with 0x02030405: LiveHop { public_key: 0x03040506, payer: Some(Payer { wallet: Wallet { kind: Address(0x71d0fc7d1c570b1ed786382b551a09391c91e33d) }, proof: Signature { v: 0, r: "4324a40295bb36ef2b927fb24250fe42397a57b861ea152bbbe4f84150d4ff5a", s: "4324a40295bb36ef2b927fb24250fe42397a57b861ea152bbbe4f84150d4ff5a" } }), component: Hopper }
Encrypted with 0x03040506: LiveHop { public_key: 0x, payer: Some(Payer { wallet: Wallet { kind: Address(0x71d0fc7d1c570b1ed786382b551a09391c91e33d) }, proof: Signature { v: 1, r: "8649b8f6db6232cb1e4f1f04786ad4ef33488c968e64bec74ecd893d6d05c1b9", s: "8649b8f6db6232cb1e4f1f04786ad4ef33488c968e64bec74ecd893d6d05c1b9" } }), component: Neighborhood }
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switching blockchains from EthRopsten to BaseSepolia resulted in different signatures.

fn default() -> Self {
Self::new()
}
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Creating a StreamKey now requires two parameters: no more Default.


type HashType = [u8; sha1::DIGEST_LENGTH];

fn add_socket_addr_to_hash(mut hash: sha1::Sha1, client_addr: SocketAddr) -> sha1::Sha1 {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wrote this whole function, just working from the name of it (and probably the tests for the caller), and I didn't modify it. Pretty cool, hey?

@dnwiebe dnwiebe merged commit f2a2470 into master Apr 1, 2025
6 checks passed
@dnwiebe dnwiebe deleted the GH-554-dan branch April 1, 2025 11:35
utkarshg6 added a commit that referenced this pull request Jul 10, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants