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

feat: encode max allowable encrypted bytes into struct #6425

Merged
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
2 changes: 1 addition & 1 deletion base_layer/core/src/consensus/consensus_constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ impl ConsensusConstants {
faucet_value: 0.into(),
transaction_weight: TransactionWeight::latest(),
max_script_byte_size: 512,
max_extra_encrypted_data_byte_size: 256,
max_extra_encrypted_data_byte_size: 240,
input_version_range,
output_version_range,
kernel_version_range,
Expand Down
33 changes: 31 additions & 2 deletions base_layer/core/src/consensus/consensus_encoding/bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,19 @@
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use std::{cmp, convert::TryFrom, ops::Deref};
use std::{
cmp,
convert::TryFrom,
ops::{Deref, DerefMut},
};

use borsh::{BorshDeserialize, BorshSerialize};
use serde::{Deserialize, Serialize};
use tari_utilities::hex::{from_hex, HexError};
use tari_utilities::{
hex::{from_hex, HexError},
ByteArray,
ByteArrayError,
};

#[derive(
Debug,
Expand Down Expand Up @@ -118,6 +126,27 @@ impl<const MAX: usize> Deref for MaxSizeBytes<MAX> {
}
}

impl<const MAX: usize> DerefMut for MaxSizeBytes<MAX> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}

impl<const MAX: usize> ByteArray for MaxSizeBytes<MAX> {
/// Try and convert the given byte array to a MaxSizeBytes. Any failures (incorrect array length,
/// implementation-specific checks, etc) return a [ByteArrayError](enum.ByteArrayError.html).
fn from_canonical_bytes(bytes: &[u8]) -> Result<Self, ByteArrayError> {
Self::from_bytes_checked(bytes).ok_or(ByteArrayError::ConversionError {
reason: "Invalid byte length".to_string(),
})
}

/// Return the NodeId as a byte array
fn as_bytes(&self) -> &[u8] {
self.inner.as_ref()
}
}

#[derive(Debug, thiserror::Error)]
pub enum MaxSizeBytesError {
#[error("Invalid Bytes length: expected {expected}, got {actual}")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
//! Encrypted data using the extended-nonce variant XChaCha20-Poly1305 encryption with secure random nonce.

use std::{
convert::TryInto,
convert::{TryFrom, TryInto},
fmt,
fmt::{Display, Formatter},
mem::size_of,
Expand Down Expand Up @@ -60,22 +60,22 @@ use thiserror::Error;
use zeroize::{Zeroize, Zeroizing};

use super::EncryptedDataKey;
use crate::transactions::tari_amount::MicroMinotari;

use crate::{consensus::MaxSizeBytes, transactions::tari_amount::MicroMinotari};
// Useful size constants, each in bytes
const SIZE_NONCE: usize = size_of::<XNonce>();
const SIZE_VALUE: usize = size_of::<u64>();
const SIZE_MASK: usize = PrivateKey::KEY_LEN;
const SIZE_TAG: usize = size_of::<Tag>();
pub const STATIC_ENCRYPTED_DATA_SIZE_TOTAL: usize = SIZE_NONCE + SIZE_VALUE + SIZE_MASK + SIZE_TAG;
const MAX_ENCRYPTED_DATA_SIZE: usize = 256 + STATIC_ENCRYPTED_DATA_SIZE_TOTAL;

// Number of hex characters of encrypted data to display on each side of ellipsis when truncating
const DISPLAY_CUTOFF: usize = 16;

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash, BorshSerialize, BorshDeserialize, Zeroize)]
pub struct EncryptedData {
#[serde(with = "tari_utilities::serde::hex")]
data: Vec<u8>,
data: MaxSizeBytes<MAX_ENCRYPTED_DATA_SIZE>,
}

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
Expand Down Expand Up @@ -185,7 +185,10 @@ impl EncryptedData {
data[SIZE_TAG + SIZE_NONCE..SIZE_TAG + SIZE_NONCE + SIZE_VALUE + SIZE_MASK + payment_id.get_size()]
.clone_from_slice(bytes.as_slice());

Ok(Self { data })
Ok(Self {
data: MaxSizeBytes::try_from(data)
.map_err(|_| EncryptedDataError::IncorrectLength("Data too long".to_string()))?,
})
}

/// Authenticate and decrypt the value and mask
Expand Down Expand Up @@ -235,19 +238,22 @@ impl EncryptedData {
bytes.len()
)));
}
let mut data = vec![0; bytes.len()];
data.copy_from_slice(bytes);
Ok(Self { data })
Ok(Self {
data: MaxSizeBytes::from_bytes_checked(bytes)
.ok_or(EncryptedDataError::IncorrectLength("Data too long".to_string()))?,
})
}

#[cfg(test)]
pub fn from_vec_unsafe(data: Vec<u8>) -> Self {
Self { data }
Self {
data: MaxSizeBytes::from_bytes_checked(data).unwrap(),
}
}

/// Get a byte vector with the encrypted data contents
pub fn to_byte_vec(&self) -> Vec<u8> {
self.data.clone()
self.data.clone().into()
}

/// Get a byte slice with the encrypted data contents
Expand Down Expand Up @@ -290,11 +296,11 @@ impl Hex for EncryptedData {
to_hex(&self.to_byte_vec())
}
}

impl Default for EncryptedData {
fn default() -> Self {
Self {
data: vec![0; STATIC_ENCRYPTED_DATA_SIZE_TOTAL],
data: MaxSizeBytes::try_from(vec![0; STATIC_ENCRYPTED_DATA_SIZE_TOTAL])
.expect("This will always be less then the max length"),
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion base_layer/core/src/validation/block_body/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ async fn it_limits_the_encrypted_data_byte_size() {
let (txs, _) = schema_to_transaction(&[schema1], &blockchain.km).await;
let mut txs = txs.into_iter().map(|t| Arc::try_unwrap(t).unwrap()).collect::<Vec<_>>();
let mut outputs = txs[0].body.outputs().clone();
outputs[0].encrypted_data = EncryptedData::from_vec_unsafe(vec![0; STATIC_ENCRYPTED_DATA_SIZE_TOTAL + 257]);
outputs[0].encrypted_data = EncryptedData::from_vec_unsafe(vec![0; STATIC_ENCRYPTED_DATA_SIZE_TOTAL + 250]);
txs[0].body = AggregateBody::new(txs[0].body.inputs().clone(), outputs, txs[0].body.kernels().clone());
let (block, _) = blockchain.create_next_tip(block_spec!("B", transactions: txs)).await;

Expand Down
Loading