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

MC-1117 8-byte masked_value #24

Merged
merged 5 commits into from
Apr 21, 2020
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
6 changes: 3 additions & 3 deletions consensus/api/proto/external.proto
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,10 @@ message Amount {
// A Pedersen commitment `v*G + s*H`
CurvePoint commitment = 1;

// `masked_value = value + SHA3-512_scalar(shared_secret || n)`
CurveScalar masked_value = 2;
// `masked_value = value XOR_8 Blake2B("value_mask" || shared_secret)`
Copy link
Contributor 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.

uint64 masked_value = 2;

// `masked_blinding = value + SHA3-512_scalar(SHA3-512_scalar(shared_secret || n))
// `masked_blinding = blinding + Blake2B("bliding_mask" || shared_secret))
CurveScalar masked_blinding = 3;
}

Expand Down
17 changes: 5 additions & 12 deletions consensus/api/src/conversions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -616,17 +616,13 @@ impl TryFrom<&external::CurvePoint> for CompressedCommitment {

impl From<&Amount> for external::Amount {
fn from(source: &Amount) -> Self {
let mut amount = external::Amount::new();

let commitment_bytes = source.commitment.to_bytes().to_vec();
amount.mut_commitment().set_data(commitment_bytes);

let masked_value_bytes = source.masked_value.as_bytes().to_vec();
amount.mut_masked_value().set_data(masked_value_bytes);

let masked_blinding_bytes = source.masked_blinding.as_bytes().to_vec();
amount.mut_masked_blinding().set_data(masked_blinding_bytes);

let mut amount = external::Amount::new();
amount.mut_commitment().set_data(commitment_bytes);
amount.set_masked_value(source.masked_value);
amount.mut_masked_blinding().set_data(masked_blinding_bytes);
amount
}
}
Expand All @@ -646,10 +642,7 @@ impl TryFrom<&external::Amount> for Amount {
Ok(CurveScalar::from_bytes_mod_order(curve_bytes))
};

let masked_value: CurveScalar = {
let bytes = source.get_masked_value().get_data();
vec_to_curve_scalar(bytes)?
};
let masked_value = source.get_masked_value();

let masked_blinding: Blinding = {
let bytes = source.get_masked_blinding().get_data();
Expand Down
79 changes: 39 additions & 40 deletions transaction/core/src/amount.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,24 @@ pub enum AmountError {
InconsistentCommitment,
}

/// Value mask hash function domain separator.
const VALUE_MASK: &str = "amount_value_mask";

/// Blinding mask hash function domain separator.
const BLINDING_MASK: &str = "amount_blinding_mask";

/// A commitment to an amount of MobileCoin, denominated in picoMOB.
#[derive(Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Message, Digestible)]
pub struct Amount {
/// A Pedersen commitment `v*G + b*H` to a quantity `v` of MobileCoin, with blinding `b`,
#[prost(message, required, tag = "1")]
pub commitment: CompressedCommitment,

/// `masked_value = value + Blake2B(shared_secret)`
#[prost(message, required, tag = "2")]
pub masked_value: CurveScalar,
/// `masked_value = value XOR_8 Blake2B(value_mask | shared_secret)`
#[prost(uint64, required, tag = "2")]
pub masked_value: u64,

/// `masked_blinding = blinding + Blake2B(Blake2B(shared_secret))
/// `masked_blinding = blinding + Blake2B(blinding_mask | shared_secret))
#[prost(message, required, tag = "3")]
pub masked_blinding: Blinding,
}
Expand All @@ -61,23 +67,27 @@ impl Amount {
// Pedersen commitment `v*G + b*H`.
let commitment = CompressedCommitment::new(value, blinding.into());

// `v + Blake2B(shared_secret)`
let masked_value: Scalar = {
let value: Scalar = Scalar::from(value);
let mask = get_value_mask(&shared_secret);
value + mask
// The value is XORed with the first 8 bytes of the mask.
// `v XOR_8 Blake2B(value_mask | shared_secret)`
let masked_value: u64 = {
let mask: u64 = {
let mut temp = [0u8; 8];
temp.copy_from_slice(&get_value_mask(&shared_secret).as_bytes()[0..8]);
u64::from_le_bytes(temp)
};
value ^ mask
};

// `s + Blake2B(Blake2B(shared_secret))`
// `b + Blake2B("blinding_mask" | shared_secret)`
let masked_blinding: Scalar = {
let mask = get_blinding_mask(&shared_secret);
blinding.as_ref() + mask
};

Ok(Amount {
commitment,
masked_value,
masked_blinding: Blinding::from(masked_blinding),
masked_value: CurveScalar::from(masked_value),
})
}

Expand Down Expand Up @@ -107,64 +117,53 @@ impl Amount {

/// Reveals `masked_value`.
fn unmask_value(&self, shared_secret: &RistrettoPublic) -> u64 {
let mask = get_value_mask(shared_secret);
let masked_value: Scalar = self.masked_value.into();
let value_as_scalar = masked_value - mask;
// TODO: better way to do this?
// We might want to give an error if scalar.as_bytes() is larger than u64
let mut temp = [0u8; 8];
temp.copy_from_slice(&value_as_scalar.as_bytes()[0..8]);
// Note: Dalek documents that scalar.as_bytes() returns in little-endian
// https://doc.dalek.rs/curve25519_dalek/scalar/struct.Scalar.html#method.as_bytes
u64::from_le_bytes(temp)
let mask: u64 = {
let mut temp = [0u8; 8];
temp.copy_from_slice(&get_value_mask(&shared_secret).as_bytes()[0..8]);
u64::from_le_bytes(temp)
};
self.masked_value ^ mask
}

/// Reveals masked_blinding.
/// Reveals `masked_blinding`.
fn unmask_blinding(&self, shared_secret: &RistrettoPublic) -> Blinding {
let mask = get_blinding_mask(shared_secret);
let masked_blinding: Scalar = self.masked_blinding.into();
Blinding::from(masked_blinding - mask)
}
}

/// Computes `Blake2B(shared_secret)`
/// Computes `Blake2B(value_mask | shared_secret)`.
///
/// # Arguments
/// * `shared_secret` - The shared secret, e.g. `rB`.
fn get_value_mask(shared_secret: &RistrettoPublic) -> Scalar {
get_mask(&shared_secret)
let mut hasher = Blake2b::new();
hasher.input(&VALUE_MASK);
hasher.input(&shared_secret.to_bytes());
Scalar::from_hash(hasher)
}

/// Computes `Blake2B(Blake2B(shared_secret)`.
/// Computes `Blake2B(blinding_mask | shared_secret)`.
///
/// # Arguments
/// * `shared_secret` - The shared secret, e.g. `rB`.
fn get_blinding_mask(shared_secret: &RistrettoPublic) -> Scalar {
let inner_mask = get_mask(shared_secret);

let mut hasher = Blake2b::new();
hasher.input(&inner_mask.to_bytes());

Scalar::from_hash(hasher)
}

/// Computes `Blake2B(shared_secret)`.
fn get_mask(shared_secret: &RistrettoPublic) -> Scalar {
let mut hasher = Blake2b::new();
hasher.input(&BLINDING_MASK);
hasher.input(&shared_secret.to_bytes());
Scalar::from_hash(hasher)
}

#[cfg(test)]
mod tests {
use crate::proptest_fixtures::*;
use proptest::prelude::*;

mod amount_tests {
use crate::{
amount::{Amount, AmountError},
proptest_fixtures::*,
ring_signature::{Scalar, GENERATORS},
CompressedCommitment,
};
use proptest::prelude::*;

proptest! {

Expand Down Expand Up @@ -236,7 +235,7 @@ mod tests {
/// get_value should return InconsistentCommitment if the masked value is incorrect.
fn test_get_value_incorrect_masked_value(
value in any::<u64>(),
other_masked_value in arbitrary_curve_scalar(),
other_masked_value in any::<u64>(),
blinding in arbitrary_blinding(),
shared_secret in arbitrary_ristretto_public())
{
Expand Down