Implement view tags

This commit is contained in:
Luke Parker 2022-07-27 06:29:14 -04:00
parent 755dc84859
commit bba93a64c2
No known key found for this signature in database
GPG key ID: F9F1386DB1E119B6
4 changed files with 44 additions and 25 deletions

View file

@ -62,7 +62,7 @@ impl Input {
pub struct Output { pub struct Output {
pub amount: u64, pub amount: u64,
pub key: EdwardsPoint, pub key: EdwardsPoint,
pub tag: Option<u8>, pub view_tag: Option<u8>,
} }
impl Output { impl Output {
@ -72,31 +72,34 @@ impl Output {
pub fn serialize<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> { pub fn serialize<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
write_varint(&self.amount, w)?; write_varint(&self.amount, w)?;
w.write_all(&[2 + (if self.tag.is_some() { 1 } else { 0 })])?; w.write_all(&[2 + (if self.view_tag.is_some() { 1 } else { 0 })])?;
write_point(&self.key, w)?; write_point(&self.key, w)?;
if let Some(tag) = self.tag { if let Some(view_tag) = self.view_tag {
w.write_all(&[tag])?; w.write_all(&[view_tag])?;
} }
Ok(()) Ok(())
} }
pub fn deserialize<R: std::io::Read>(r: &mut R) -> std::io::Result<Output> { pub fn deserialize<R: std::io::Read>(r: &mut R) -> std::io::Result<Output> {
let amount = read_varint(r)?; let amount = read_varint(r)?;
let mut tag = [0]; let mut output_type = [0];
r.read_exact(&mut tag)?; r.read_exact(&mut output_type)?;
if (tag[0] != 2) && (tag[0] != 3) { let view_tag = match output_type[0] {
Err(std::io::Error::new( 2 => false,
3 => true,
_ => Err(std::io::Error::new(
std::io::ErrorKind::Other, std::io::ErrorKind::Other,
"Tried to deserialize unknown/unused output type", "Tried to deserialize unknown/unused output type",
))?; ))?,
} };
Ok(Output { Ok(Output {
amount, amount,
key: read_point(r)?, key: read_point(r)?,
tag: if tag[0] == 3 { view_tag: if view_tag {
r.read_exact(&mut tag)?; let mut view_tag = [0];
Some(tag[0]) r.read_exact(&mut view_tag)?;
Some(view_tag[0])
} else { } else {
None None
}, },

View file

@ -35,22 +35,29 @@ pub(crate) fn uniqueness(inputs: &[Input]) -> [u8; 32] {
hash(&u) hash(&u)
} }
// Hs(8Ra || o) with https://github.com/monero-project/research-lab/issues/103 as an option // Hs("view_tag" || 8Ra || o) and Hs(8Ra || o) with uniqueness inclusion as an option
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub(crate) fn shared_key( pub(crate) fn shared_key(
uniqueness: Option<[u8; 32]>, uniqueness: Option<[u8; 32]>,
s: Scalar, s: Scalar,
P: &EdwardsPoint, P: &EdwardsPoint,
o: usize, o: usize,
) -> Scalar { ) -> (u8, Scalar) {
// uniqueness // 8Ra
let mut shared = uniqueness.map_or(vec![], |uniqueness| uniqueness.to_vec()); let mut output_derivation = (s * P).mul_by_cofactor().compress().to_bytes().to_vec();
// || 8Ra
shared.extend((s * P).mul_by_cofactor().compress().to_bytes().to_vec());
// || o // || o
write_varint(&o.try_into().unwrap(), &mut shared).unwrap(); write_varint(&o.try_into().unwrap(), &mut output_derivation).unwrap();
// Hs()
hash_to_scalar(&shared) let view_tag = hash(&[b"view_tag".as_ref(), &output_derivation].concat())[0];
// uniqueness ||
let shared_key = if let Some(uniqueness) = uniqueness {
[uniqueness.as_ref(), &output_derivation].concat().to_vec()
} else {
output_derivation
};
(view_tag, hash_to_scalar(&shared_key))
} }
pub(crate) fn amount_encryption(amount: u64, key: Scalar) -> [u8; 8] { pub(crate) fn amount_encryption(amount: u64, key: Scalar) -> [u8; 8] {

View file

@ -101,12 +101,19 @@ impl Transaction {
for (o, output) in self.prefix.outputs.iter().enumerate() { for (o, output) in self.prefix.outputs.iter().enumerate() {
// TODO: This may be replaceable by pubkeys[o] // TODO: This may be replaceable by pubkeys[o]
for pubkey in &pubkeys { for pubkey in &pubkeys {
let key_offset = shared_key( let (view_tag, key_offset) = shared_key(
Some(uniqueness(&self.prefix.inputs)).filter(|_| guaranteed), Some(uniqueness(&self.prefix.inputs)).filter(|_| guaranteed),
view.view, view.view,
pubkey, pubkey,
o, o,
); );
if let Some(actual_view_tag) = output.view_tag {
if actual_view_tag != view_tag {
continue;
}
}
// P - shared == spend // P - shared == spend
if (output.key - (&key_offset * &ED25519_BASEPOINT_TABLE)) != view.spend { if (output.key - (&key_offset * &ED25519_BASEPOINT_TABLE)) != view.spend {
continue; continue;

View file

@ -38,6 +38,7 @@ pub use multisig::TransactionMachine;
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
struct SendOutput { struct SendOutput {
R: EdwardsPoint, R: EdwardsPoint,
view_tag: u8,
dest: EdwardsPoint, dest: EdwardsPoint,
commitment: Commitment, commitment: Commitment,
amount: [u8; 8], amount: [u8; 8],
@ -51,7 +52,7 @@ impl SendOutput {
o: usize, o: usize,
) -> SendOutput { ) -> SendOutput {
let r = random_scalar(rng); let r = random_scalar(rng);
let shared_key = let (view_tag, shared_key) =
shared_key(Some(unique).filter(|_| output.0.meta.guaranteed), r, &output.0.view, o); shared_key(Some(unique).filter(|_| output.0.meta.guaranteed), r, &output.0.view, o);
let spend = output.0.spend; let spend = output.0.spend;
@ -63,6 +64,7 @@ impl SendOutput {
} }
AddressType::Subaddress => r * spend, AddressType::Subaddress => r * spend,
}, },
view_tag,
dest: ((&shared_key * &ED25519_BASEPOINT_TABLE) + spend), dest: ((&shared_key * &ED25519_BASEPOINT_TABLE) + spend),
commitment: Commitment::new(commitment_mask(shared_key), output.1), commitment: Commitment::new(commitment_mask(shared_key), output.1),
amount: amount_encryption(output.1, shared_key), amount: amount_encryption(output.1, shared_key),
@ -297,7 +299,7 @@ impl SignableTransaction {
tx_outputs.push(Output { tx_outputs.push(Output {
amount: 0, amount: 0,
key: self.outputs[o].dest, key: self.outputs[o].dest,
tag: Some(0).filter(|_| matches!(self.protocol, Protocol::v16)), view_tag: Some(self.outputs[o].view_tag).filter(|_| matches!(self.protocol, Protocol::v16)),
}); });
ecdh_info.push(self.outputs[o].amount); ecdh_info.push(self.outputs[o].amount);
} }