Correct handling of commitment masks when scanning

This commit is contained in:
Luke Parker
2023-07-03 19:04:33 -04:00
parent 81cfd841ce
commit 26fdc1d1f1
2 changed files with 19 additions and 12 deletions

View File

@@ -93,28 +93,36 @@ pub(crate) fn amount_encryption(amount: u64, key: Scalar) -> [u8; 8] {
(amount ^ u64::from_le_bytes(hash(&amount_mask)[.. 8].try_into().unwrap())).to_le_bytes()
}
fn amount_decryption(amount: &EncryptedAmount, key: Scalar) -> u64 {
// TODO: Move this under EncryptedAmount?
fn amount_decryption(amount: &EncryptedAmount, key: Scalar) -> (Scalar, u64) {
match amount {
EncryptedAmount::Original { mask: _, amount } => {
EncryptedAmount::Original { mask, amount } => {
#[cfg(feature = "experimental")]
{
let shared_sec = hash(&hash(key.as_bytes()));
let mask_shared_sec = hash(key.as_bytes());
let mask =
Scalar::from_bytes_mod_order(*mask) - Scalar::from_bytes_mod_order(mask_shared_sec);
let amount_shared_sec = hash(&mask_shared_sec);
let amount_scalar =
Scalar::from_bytes_mod_order(*amount) - Scalar::from_bytes_mod_order(shared_sec);
Scalar::from_bytes_mod_order(*amount) - Scalar::from_bytes_mod_order(amount_shared_sec);
// d2b from rctTypes.cpp
let amount_significant_bytes = amount_scalar.to_bytes()[0 .. 8].try_into().unwrap();
u64::from_le_bytes(amount_significant_bytes)
let amount = u64::from_le_bytes(amount_scalar.to_bytes()[0 .. 8].try_into().unwrap());
(mask, amount)
}
#[cfg(not(feature = "experimental"))]
{
let _ = mask;
let _ = amount;
todo!("decrypting a legacy monero transaction's amount")
}
}
EncryptedAmount::Compact { amount } => {
u64::from_le_bytes(amount_encryption(u64::from_le_bytes(*amount), key))
}
EncryptedAmount::Compact { amount } => (
commitment_mask(key),
u64::from_le_bytes(amount_encryption(u64::from_le_bytes(*amount), key)),
),
}
}

View File

@@ -16,7 +16,6 @@ use crate::{
rpc::{RpcError, RpcConnection, Rpc},
wallet::{
PaymentId, Extra, address::SubaddressIndex, Scanner, uniqueness, shared_key, amount_decryption,
commitment_mask,
},
};
@@ -379,7 +378,7 @@ impl Scanner {
commitment.amount = amount;
// Regular transaction
} else {
let amount = match tx.rct_signatures.base.encrypted_amounts.get(o) {
let (mask, amount) = match tx.rct_signatures.base.encrypted_amounts.get(o) {
Some(amount) => amount_decryption(amount, shared_key),
// This should never happen, yet it may be possible with miner transactions?
// Using get just decreases the possibility of a panic and lets us move on in that case
@@ -387,7 +386,7 @@ impl Scanner {
};
// Rebuild the commitment to verify it
commitment = Commitment::new(commitment_mask(shared_key), amount);
commitment = Commitment::new(mask, amount);
// If this is a malicious commitment, move to the next output
// Any other R value will calculate to a different spend key and are therefore ignorable
if Some(&commitment.calculate()) != tx.rct_signatures.base.commitments.get(o) {