Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Re-enable noise encryption #2700

Closed
wants to merge 13 commits into from
4 changes: 4 additions & 0 deletions node/bft/events/fuzz/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
target
corpus
artifacts
coverage
61 changes: 61 additions & 0 deletions node/bft/events/fuzz/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
[package]
name = "snarkos-node-bft-events-fuzz"
version = "2.1.7"
publish = false
authors = [ "The Aleo Team <hello@aleo.org>" ]
description = "Event fuzzing for the gateway in a decentralized operating system"
homepage = "https://aleo.org"
repository = "https://github.com/AleoHQ/snarkOS"
keywords = [
"aleo",
"cryptography",
"blockchain",
"decentralized",
"zero-knowledge"
]
categories = [ "cryptography", "operating-systems" ]
license = "Apache-2.0"
edition = "2021"

[package.metadata]
cargo-fuzz = true

[dependencies.bytes]
version = "1"

[dependencies.libfuzzer-sys]
version = "0.4"

[dependencies.once_cell]
version = "1"

[dependencies.rand]
version = "0.8"

[dependencies.snarkos-node-bft-events]
path = ".."

[dependencies.snarkvm]
git = "https://github.com/AleoHQ/snarkVM.git"
rev = "b396a51"
features = [ "circuit", "console", "rocks" ]

[dependencies.snow]
version = "0.9"

[dependencies.tokio-util]
version = "0.7"
features = [ "codec" ]

# Prevent this from interfering with workspaces
[workspace]
members = ["."]

[profile.release]
debug = 1

[[bin]]
name = "codec"
path = "fuzz_targets/codec.rs"
test = false
doc = false
92 changes: 92 additions & 0 deletions node/bft/events/fuzz/fuzz_targets/codec.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// Copyright (C) 2019-2023 Aleo Systems Inc.
// This file is part of the snarkOS library.

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
// http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![no_main]

use std::sync::Mutex;

use snarkos_node_bft_events::{
Event, EventOrBytes, NoiseCodec, NoiseState, TransmissionResponse, NOISE_HANDSHAKE_TYPE,
};
use snarkvm::{
ledger::narwhal::{Data, Transmission, TransmissionID},
prelude::{Field, Network, TestRng, Uniform},
};

use bytes::{Bytes, BytesMut};
use libfuzzer_sys::fuzz_target;
use once_cell::sync::OnceCell;
use snow::{params::NoiseParams, Builder};
use tokio_util::codec::{Decoder, Encoder};

type CurrentNetwork = snarkvm::console::network::MainnetV0;

static RNG: OnceCell<Mutex<TestRng>> = OnceCell::new();
static CODECS: OnceCell<Mutex<(NoiseCodec<CurrentNetwork>, NoiseCodec<CurrentNetwork>)>> = OnceCell::new();

fuzz_target!(|data: &[u8]| {
let mut rng = &mut *RNG.get_or_init(|| Default::default()).lock().unwrap();

let codecs = CODECS.get_or_init(|| handshake_xx());
let (initiator_codec, responder_codec) = &mut *codecs.lock().unwrap();
let mut ciphertext = BytesMut::new();

let id = TransmissionID::Transaction(<CurrentNetwork as Network>::TransactionID::from(Field::rand(&mut rng)));
let transmission = Transmission::Transaction(Data::Buffer(Bytes::copy_from_slice(data)));

let transmission_response = TransmissionResponse::new(id, transmission);
let msg = EventOrBytes::Event(Event::TransmissionResponse(transmission_response));

assert!(initiator_codec.encode(msg.clone(), &mut ciphertext).is_ok());
assert_eq!(responder_codec.decode(&mut ciphertext).unwrap().unwrap(), msg);
});

fn handshake_xx() -> Mutex<(NoiseCodec<CurrentNetwork>, NoiseCodec<CurrentNetwork>)> {
let params: NoiseParams = NOISE_HANDSHAKE_TYPE.parse().unwrap();
let initiator_builder = Builder::new(params.clone());
let initiator_kp = initiator_builder.generate_keypair().unwrap();
let initiator = initiator_builder.local_private_key(&initiator_kp.private).build_initiator().unwrap();

let responder_builder = Builder::new(params);
let responder_kp = responder_builder.generate_keypair().unwrap();
let responder = responder_builder.local_private_key(&responder_kp.private).build_responder().unwrap();

let mut initiator_codec = NoiseCodec::new(NoiseState::Handshake(Box::new(initiator)));
let mut responder_codec = NoiseCodec::new(NoiseState::Handshake(Box::new(responder)));

let mut ciphertext = BytesMut::new();

// -> e
assert!(initiator_codec.encode(EventOrBytes::Bytes(Bytes::new()), &mut ciphertext).is_ok());
assert!(
matches!(responder_codec.decode(&mut ciphertext).unwrap().unwrap(), EventOrBytes::Bytes(bytes) if bytes.is_empty())
);

// <- e, ee, s, es
assert!(responder_codec.encode(EventOrBytes::Bytes(Bytes::new()), &mut ciphertext).is_ok());
assert!(
matches!(initiator_codec.decode(&mut ciphertext).unwrap().unwrap(), EventOrBytes::Bytes(bytes) if bytes.is_empty())
);

// -> s, se
assert!(initiator_codec.encode(EventOrBytes::Bytes(Bytes::new()), &mut ciphertext).is_ok());
assert!(
matches!(responder_codec.decode(&mut ciphertext).unwrap().unwrap(), EventOrBytes::Bytes(bytes) if bytes.is_empty())
);

initiator_codec.noise_state = initiator_codec.noise_state.into_post_handshake_state();
responder_codec.noise_state = responder_codec.noise_state.into_post_handshake_state();

Mutex::new((initiator_codec, responder_codec))
}
25 changes: 23 additions & 2 deletions node/bft/events/src/helpers/codec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,11 @@ pub struct NoiseCodec<N: Network> {

impl<N: Network> NoiseCodec<N> {
pub fn new(noise_state: NoiseState) -> Self {
Self { codec: LengthDelimitedCodec::new(), event_codec: EventCodec::default(), noise_state }
Self {
codec: LengthDelimitedCodec::builder().max_frame_length(MAX_EVENT_SIZE).little_endian().new_codec(),
event_codec: EventCodec::default(),
noise_state,
}
}
}

Expand Down Expand Up @@ -340,7 +344,11 @@ impl<N: Network> Decoder for NoiseCodec<N> {
#[cfg(test)]
mod tests {
use super::*;
use crate::prop_tests::any_event;
use crate::{
prop_tests::any_event,
transmission_response::prop_tests::any_large_transmission_response,
TransmissionResponse,
};

use snow::{params::NoiseParams, Builder};
use test_strategy::proptest;
Expand Down Expand Up @@ -399,4 +407,17 @@ mod tests {
fn event_roundtrip(#[strategy(any_event())] event: Event<CurrentNetwork>) {
assert_roundtrip(EventOrBytes::Event(event))
}

#[proptest]
fn rayon_chunking_roundtrip(
#[strategy(any_large_transmission_response())] transmission: TransmissionResponse<CurrentNetwork>,
) {
let (mut initiator_codec, mut responder_codec) = handshake_xx();
let mut ciphertext = BytesMut::new();

let msg = EventOrBytes::Event(Event::TransmissionResponse(transmission.clone()));

assert!(initiator_codec.encode(msg.clone(), &mut ciphertext).is_ok());
assert_eq!(responder_codec.decode(&mut ciphertext).unwrap().unwrap(), msg);
}
}
19 changes: 19 additions & 0 deletions node/bft/events/src/transmission_response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,25 @@ pub mod prop_tests {
any_transmission().prop_map(|(id, t)| TransmissionResponse::new(id, t)).boxed()
}

// TODO(nkls): dedup this?
pub fn any_large_transmission() -> BoxedStrategy<(TransmissionID<CurrentNetwork>, Transmission<CurrentNetwork>)> {
prop_oneof![
(any_puzzle_commitment(), collection::vec(any::<u8>(), 65535..=65535 * 5)).prop_map(|(pc, bytes)| (
TransmissionID::Solution(pc),
Transmission::Solution(Data::Buffer(Bytes::from(bytes)))
)),
(any_transaction_id(), collection::vec(any::<u8>(), 65535..=65353 * 5)).prop_map(|(tid, bytes)| (
TransmissionID::Transaction(tid),
Transmission::Transaction(Data::Buffer(Bytes::from(bytes)))
)),
]
.boxed()
}

pub fn any_large_transmission_response() -> BoxedStrategy<TransmissionResponse<CurrentNetwork>> {
any_large_transmission().prop_map(|(id, t)| TransmissionResponse::new(id, t)).boxed()
}

#[proptest]
fn serialize_deserialize(#[strategy(any_transmission_response())] original: TransmissionResponse<CurrentNetwork>) {
let mut buf = BytesMut::default().writer();
Expand Down