diff --git a/fog/sample-paykit/src/cached_tx_data/memo_handler.rs b/fog/sample-paykit/src/cached_tx_data/memo_handler.rs index 2ad43a449f..5b950d25aa 100644 --- a/fog/sample-paykit/src/cached_tx_data/memo_handler.rs +++ b/fog/sample-paykit/src/cached_tx_data/memo_handler.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2018-2022 The MobileCoin Foundation +// Copyright (c) 2018-2023 The MobileCoin Foundation //! A memo handler object which processes memos, for use in integration tests @@ -86,7 +86,7 @@ impl MemoHandler { Err(MemoHandlerError::UnknownSender) } } - MemoType::AuthenticatedSenderWithPaymentRequestId(memo) => { + MemoType::AuthenticatedSenderWithPaymentIntentId(memo) => { if let Some(addr) = self.contacts.get(&memo.sender_address_hash()) { if bool::from(memo.validate( addr, @@ -101,26 +101,7 @@ impl MemoHandler { Err(MemoHandlerError::UnknownSender) } } - MemoType::Destination(_) => { - if subaddress_matches_tx_out(account_key, CHANGE_SUBADDRESS_INDEX, tx_out)? { - Ok(Some(memo_type)) - } else { - Err(MemoHandlerError::FailedSubaddressValidation) - } - } - MemoType::GiftCodeCancellation(_) => { - // TODO: Add Gift Code Memo Validation - Ok(Some(memo_type)) - } - MemoType::GiftCodeFunding(_) => { - // TODO: Add Gift Code Memo Validation - Ok(Some(memo_type)) - } - MemoType::GiftCodeSender(_) => { - // TODO: Add Gift Code Validation - Ok(Some(memo_type)) - } - MemoType::AuthenticatedSenderWithPaymentIntentId(memo) => { + MemoType::AuthenticatedSenderWithPaymentRequestId(memo) => { if let Some(addr) = self.contacts.get(&memo.sender_address_hash()) { if bool::from(memo.validate( addr, @@ -135,6 +116,13 @@ impl MemoHandler { Err(MemoHandlerError::UnknownSender) } } + MemoType::Destination(_) => { + if subaddress_matches_tx_out(account_key, CHANGE_SUBADDRESS_INDEX, tx_out)? { + Ok(Some(memo_type)) + } else { + Err(MemoHandlerError::FailedSubaddressValidation) + } + } MemoType::DestinationWithPaymentRequestId(_) => { if subaddress_matches_tx_out(account_key, CHANGE_SUBADDRESS_INDEX, tx_out)? { Ok(Some(memo_type)) @@ -156,6 +144,18 @@ impl MemoHandler { Err(MemoHandlerError::FailedSubaddressValidation) } } + MemoType::GiftCodeCancellation(_) => { + // TODO: Add Gift Code Memo Validation + Ok(Some(memo_type)) + } + MemoType::GiftCodeFunding(_) => { + // TODO: Add Gift Code Memo Validation + Ok(Some(memo_type)) + } + MemoType::GiftCodeSender(_) => { + // TODO: Add Gift Code Validation + Ok(Some(memo_type)) + } } } } diff --git a/transaction/extra/src/lib.rs b/transaction/extra/src/lib.rs index 1c4d13e0b2..65157d283f 100644 --- a/transaction/extra/src/lib.rs +++ b/transaction/extra/src/lib.rs @@ -19,10 +19,10 @@ mod tx_out_gift_code; mod unsigned_tx; pub use memo::{ - AuthenticatedSenderMemo, AuthenticatedSenderWithPaymentIntentIdMemo, - AuthenticatedSenderWithPaymentRequestIdMemo, BurnRedemptionMemo, DefragmentationMemo, - DefragmentationMemoError, DestinationMemo, DestinationMemoError, - DestinationWithPaymentIntentIdMemo, DestinationWithPaymentRequestIdMemo, + compute_authenticated_sender_memo, compute_destination_memo, AuthenticatedSenderMemo, + AuthenticatedSenderWithPaymentIntentIdMemo, AuthenticatedSenderWithPaymentRequestIdMemo, + BurnRedemptionMemo, DefragmentationMemo, DefragmentationMemoError, DestinationMemo, + DestinationMemoError, DestinationWithPaymentIntentIdMemo, DestinationWithPaymentRequestIdMemo, GiftCodeCancellationMemo, GiftCodeFundingMemo, GiftCodeSenderMemo, MemoDecodingError, MemoType, RegisteredMemoType, SenderMemoCredential, UnusedMemo, }; diff --git a/transaction/extra/src/memo/authenticated_common.rs b/transaction/extra/src/memo/authenticated_common.rs index 6abb3366af..54bd69fd1a 100644 --- a/transaction/extra/src/memo/authenticated_common.rs +++ b/transaction/extra/src/memo/authenticated_common.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2018-2022 The MobileCoin Foundation +// Copyright (c) 2018-2023 The MobileCoin Foundation //! HMAC code shared by all category 0x01 memos. //! @@ -7,10 +7,14 @@ use hmac::{Hmac, Mac}; use mc_account_keys::{PublicAddress, ShortAddressHash}; -use mc_crypto_keys::{CompressedRistrettoPublic, KexReusablePrivate, RistrettoPrivate}; +use mc_crypto_keys::{ + CompressedRistrettoPublic, KexReusablePrivate, RistrettoPrivate, RistrettoPublic, +}; use sha2::Sha512; use subtle::{Choice, ConstantTimeEq}; +use crate::SenderMemoCredential; + type HmacSha512 = Hmac; /// Shared code for memo types in category 0x01, whose last 16 bytes is an HMAC @@ -70,3 +74,30 @@ pub fn validate_authenticated_sender( result &= expected_hmac.ct_eq(&found_hmac); result } + +/// Shared code for creation of an authenticated sender memo with additional +/// data +pub fn compute_authenticated_sender_memo( + memo_type_bytes: [u8; 2], + cred: &SenderMemoCredential, + receiving_subaddress_view_public_key: &RistrettoPublic, + tx_out_public_key: &CompressedRistrettoPublic, + data: &[u8], +) -> [u8; 64] { + let mut memo_data = [0u8; 64]; + memo_data[..16].copy_from_slice(cred.address_hash.as_ref()); + memo_data[16..48].copy_from_slice(data); + + let shared_secret = cred + .subaddress_spend_private_key + .key_exchange(receiving_subaddress_view_public_key); + + let hmac_value = compute_category1_hmac( + shared_secret.as_ref(), + tx_out_public_key, + memo_type_bytes, + &memo_data, + ); + memo_data[48..].copy_from_slice(&hmac_value); + memo_data +} diff --git a/transaction/extra/src/memo/authenticated_sender.rs b/transaction/extra/src/memo/authenticated_sender.rs index d7f8b42def..81c77401a3 100644 --- a/transaction/extra/src/memo/authenticated_sender.rs +++ b/transaction/extra/src/memo/authenticated_sender.rs @@ -1,19 +1,17 @@ -// Copyright (c) 2018-2022 The MobileCoin Foundation +// Copyright (c) 2018-2023 The MobileCoin Foundation //! Object for 0x0100 Authenticated Sender memo type //! //! This was proposed for standardization in mobilecoinfoundation/mcips/pull/4 use super::{ - authenticated_common::{compute_category1_hmac, validate_authenticated_sender}, + authenticated_common::{compute_authenticated_sender_memo, validate_authenticated_sender}, credential::SenderMemoCredential, RegisteredMemoType, }; use crate::impl_memo_type_conversions; use mc_account_keys::{PublicAddress, ShortAddressHash}; -use mc_crypto_keys::{ - CompressedRistrettoPublic, KexReusablePrivate, RistrettoPrivate, RistrettoPublic, -}; +use mc_crypto_keys::{CompressedRistrettoPublic, RistrettoPrivate, RistrettoPublic}; use subtle::Choice; /// A memo that the sender writes to convey their identity in an authenticated @@ -55,21 +53,14 @@ impl AuthenticatedSenderMemo { // [0-16) address hash // [16-48) unused // [48-64) HMAC - - let mut memo_data = [0u8; 64]; - memo_data[..16].copy_from_slice(cred.address_hash.as_ref()); - - let shared_secret = cred - .subaddress_spend_private_key - .key_exchange(receiving_subaddress_view_public_key); - - let hmac_value = compute_category1_hmac( - shared_secret.as_ref(), - tx_out_public_key, + let data = [0u8; (48 - 16)]; + let memo_data = compute_authenticated_sender_memo( Self::MEMO_TYPE_BYTES, - &memo_data, + cred, + receiving_subaddress_view_public_key, + tx_out_public_key, + &data, ); - memo_data[48..].copy_from_slice(&hmac_value); Self { memo_data } } diff --git a/transaction/extra/src/memo/authenticated_sender_with_payment_intent_id.rs b/transaction/extra/src/memo/authenticated_sender_with_payment_intent_id.rs index 33829c55f8..ea54adda3c 100644 --- a/transaction/extra/src/memo/authenticated_sender_with_payment_intent_id.rs +++ b/transaction/extra/src/memo/authenticated_sender_with_payment_intent_id.rs @@ -1,19 +1,17 @@ -// Copyright (c) 2018-2022 The MobileCoin Foundation +// Copyright (c) 2018-2023 The MobileCoin Foundation //! Object for 0x0102 Authenticated Sender With Payment Intent Id memo type //! //! This was proposed for standardization in mobilecoinfoundation/mcips/pull/54 use super::{ - authenticated_common::{compute_category1_hmac, validate_authenticated_sender}, + authenticated_common::{compute_authenticated_sender_memo, validate_authenticated_sender}, credential::SenderMemoCredential, RegisteredMemoType, }; use crate::impl_memo_type_conversions; use mc_account_keys::{PublicAddress, ShortAddressHash}; -use mc_crypto_keys::{ - CompressedRistrettoPublic, KexReusablePrivate, RistrettoPrivate, RistrettoPublic, -}; +use mc_crypto_keys::{CompressedRistrettoPublic, RistrettoPrivate, RistrettoPublic}; use subtle::Choice; /// A memo that the sender writes to convey their identity in an authenticated @@ -62,21 +60,16 @@ impl AuthenticatedSenderWithPaymentIntentIdMemo { // [24-48) unused // [48-64) HMAC - let mut memo_data = [0u8; 64]; - memo_data[..16].copy_from_slice(cred.address_hash.as_ref()); - memo_data[16..24].copy_from_slice(&payment_intent_id.to_be_bytes()); - - let shared_secret = cred - .subaddress_spend_private_key - .key_exchange(receiving_subaddress_view_public_key); + let mut data = [0u8; (48 - 16)]; + data[0..8].copy_from_slice(&payment_intent_id.to_be_bytes()); - let hmac_value = compute_category1_hmac( - shared_secret.as_ref(), - tx_out_public_key, + let memo_data = compute_authenticated_sender_memo( Self::MEMO_TYPE_BYTES, - &memo_data, + cred, + receiving_subaddress_view_public_key, + tx_out_public_key, + &data, ); - memo_data[48..].copy_from_slice(&hmac_value); Self { memo_data } } diff --git a/transaction/extra/src/memo/authenticated_sender_with_payment_request_id.rs b/transaction/extra/src/memo/authenticated_sender_with_payment_request_id.rs index 5199e510a9..1c446f5aa6 100644 --- a/transaction/extra/src/memo/authenticated_sender_with_payment_request_id.rs +++ b/transaction/extra/src/memo/authenticated_sender_with_payment_request_id.rs @@ -1,19 +1,17 @@ -// Copyright (c) 2018-2022 The MobileCoin Foundation +// Copyright (c) 2018-2023 The MobileCoin Foundation //! Object for 0x0101 Authenticated Sender With Payment Request Id memo type //! //! This was proposed for standardization in mobilecoinfoundation/mcips/pull/4 use super::{ - authenticated_common::{compute_category1_hmac, validate_authenticated_sender}, + authenticated_common::{compute_authenticated_sender_memo, validate_authenticated_sender}, credential::SenderMemoCredential, RegisteredMemoType, }; use crate::impl_memo_type_conversions; use mc_account_keys::{PublicAddress, ShortAddressHash}; -use mc_crypto_keys::{ - CompressedRistrettoPublic, KexReusablePrivate, RistrettoPrivate, RistrettoPublic, -}; +use mc_crypto_keys::{CompressedRistrettoPublic, RistrettoPrivate, RistrettoPublic}; use subtle::Choice; /// A memo that the sender writes to convey their identity in an authenticated @@ -60,21 +58,15 @@ impl AuthenticatedSenderWithPaymentRequestIdMemo { // [24-48) unused // [48-64) HMAC - let mut memo_data = [0u8; 64]; - memo_data[..16].copy_from_slice(cred.address_hash.as_ref()); - memo_data[16..24].copy_from_slice(&payment_request_id.to_be_bytes()); - - let shared_secret = cred - .subaddress_spend_private_key - .key_exchange(receiving_subaddress_view_public_key); - - let hmac_value = compute_category1_hmac( - shared_secret.as_ref(), - tx_out_public_key, + let mut data = [0u8; (48 - 16)]; + data[0..8].copy_from_slice(&payment_request_id.to_be_bytes()); + let memo_data = compute_authenticated_sender_memo( Self::MEMO_TYPE_BYTES, - &memo_data, + cred, + receiving_subaddress_view_public_key, + tx_out_public_key, + &data, ); - memo_data[48..].copy_from_slice(&hmac_value); Self { memo_data } } diff --git a/transaction/extra/src/memo/destination.rs b/transaction/extra/src/memo/destination.rs index 8da804b877..f0395a6b7d 100644 --- a/transaction/extra/src/memo/destination.rs +++ b/transaction/extra/src/memo/destination.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2018-2022 The MobileCoin Foundation +// Copyright (c) 2018-2023 The MobileCoin Foundation //! Object for 0x0200 Destination memo type //! @@ -141,12 +141,14 @@ impl From<&[u8; 64]> for DestinationMemo { impl From for [u8; 64] { fn from(src: DestinationMemo) -> [u8; 64] { - let mut memo_data = [0u8; 64]; - memo_data[0..16].copy_from_slice(src.address_hash.as_ref()); - memo_data[16..24].copy_from_slice(&src.fee.to_be_bytes()); - memo_data[16] = src.num_recipients; - memo_data[24..32].copy_from_slice(&src.total_outlay.to_be_bytes()); - memo_data + let data = [0u8; 32]; + compute_destination_memo( + src.address_hash, + src.fee, + src.num_recipients, + src.total_outlay, + data, + ) } } @@ -157,4 +159,22 @@ pub enum DestinationMemoError { FeeTooLarge, } +/// Shared code for creation of an destination memo with additional +/// data +pub fn compute_destination_memo( + address_hash: ShortAddressHash, + fee: u64, + num_recipients: u8, + total_outlay: u64, + data: [u8; 32], +) -> [u8; 64] { + let mut memo_data = [0u8; 64]; + memo_data[0..16].copy_from_slice(address_hash.as_ref()); + memo_data[16..24].copy_from_slice(&fee.to_be_bytes()); + memo_data[16] = num_recipients; + memo_data[24..32].copy_from_slice(&total_outlay.to_be_bytes()); + memo_data[32..64].copy_from_slice(&data); + memo_data +} + impl_memo_type_conversions! { DestinationMemo } diff --git a/transaction/extra/src/memo/destination_with_payment_intent_id.rs b/transaction/extra/src/memo/destination_with_payment_intent_id.rs index 7f01053ac2..222054a329 100644 --- a/transaction/extra/src/memo/destination_with_payment_intent_id.rs +++ b/transaction/extra/src/memo/destination_with_payment_intent_id.rs @@ -1,10 +1,10 @@ -// Copyright (c) 2018-2022 The MobileCoin Foundation +// Copyright (c) 2018-2023 The MobileCoin Foundation //! Object for 0x0204 Destination With Payment Intent Id memo type //! //! This was proposed for standardization in mobilecoinfoundation/mcips/pull/54 -use super::{DestinationMemoError, RegisteredMemoType}; +use super::{compute_destination_memo, DestinationMemoError, RegisteredMemoType}; use crate::impl_memo_type_conversions; use mc_account_keys::ShortAddressHash; @@ -165,13 +165,15 @@ impl From<&[u8; 64]> for DestinationWithPaymentIntentIdMemo { impl From for [u8; 64] { fn from(src: DestinationWithPaymentIntentIdMemo) -> [u8; 64] { - let mut memo_data = [0u8; 64]; - memo_data[0..16].copy_from_slice(src.address_hash.as_ref()); - memo_data[16..24].copy_from_slice(&src.fee.to_be_bytes()); - memo_data[16] = src.num_recipients; - memo_data[24..32].copy_from_slice(&src.total_outlay.to_be_bytes()); - memo_data[32..40].copy_from_slice(&src.payment_intent_id.to_be_bytes()); - memo_data + let mut data = [0u8; 32]; + data[0..8].copy_from_slice(&src.payment_intent_id.to_be_bytes()); + compute_destination_memo( + src.address_hash, + src.fee, + src.num_recipients, + src.total_outlay, + data, + ) } } diff --git a/transaction/extra/src/memo/destination_with_payment_request_id.rs b/transaction/extra/src/memo/destination_with_payment_request_id.rs index 13b488e4ad..e5711acf28 100644 --- a/transaction/extra/src/memo/destination_with_payment_request_id.rs +++ b/transaction/extra/src/memo/destination_with_payment_request_id.rs @@ -1,10 +1,10 @@ -// Copyright (c) 2018-2022 The MobileCoin Foundation +// Copyright (c) 2018-2023 The MobileCoin Foundation //! Object for 0x0203 Destination With Payment Request Id memo type //! //! This was proposed for standardization in mobilecoinfoundation/mcips/pull/54 -use super::{DestinationMemoError, RegisteredMemoType}; +use super::{compute_destination_memo, DestinationMemoError, RegisteredMemoType}; use crate::impl_memo_type_conversions; use mc_account_keys::ShortAddressHash; @@ -165,13 +165,15 @@ impl From<&[u8; 64]> for DestinationWithPaymentRequestIdMemo { impl From for [u8; 64] { fn from(src: DestinationWithPaymentRequestIdMemo) -> [u8; 64] { - let mut memo_data = [0u8; 64]; - memo_data[0..16].copy_from_slice(src.address_hash.as_ref()); - memo_data[16..24].copy_from_slice(&src.fee.to_be_bytes()); - memo_data[16] = src.num_recipients; - memo_data[24..32].copy_from_slice(&src.total_outlay.to_be_bytes()); - memo_data[32..40].copy_from_slice(&src.payment_request_id.to_be_bytes()); - memo_data + let mut data = [0u8; 32]; + data[0..8].copy_from_slice(&src.payment_request_id.to_be_bytes()); + compute_destination_memo( + src.address_hash, + src.fee, + src.num_recipients, + src.total_outlay, + data, + ) } } diff --git a/transaction/extra/src/memo/mod.rs b/transaction/extra/src/memo/mod.rs index 5a6562beb9..38a0e7d9fd 100644 --- a/transaction/extra/src/memo/mod.rs +++ b/transaction/extra/src/memo/mod.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2018-2022 The MobileCoin Foundation +// Copyright (c) 2018-2023 The MobileCoin Foundation //! Defines an object for each known high-level memo type, //! and an enum to allow matching recovered memos to one of these types. @@ -48,14 +48,14 @@ //! | 0x0204 | Destination With Payment Intent Id Memo | pub use self::{ - authenticated_common::compute_category1_hmac, + authenticated_common::{compute_authenticated_sender_memo, compute_category1_hmac}, authenticated_sender::AuthenticatedSenderMemo, authenticated_sender_with_payment_intent_id::AuthenticatedSenderWithPaymentIntentIdMemo, authenticated_sender_with_payment_request_id::AuthenticatedSenderWithPaymentRequestIdMemo, burn_redemption::BurnRedemptionMemo, credential::SenderMemoCredential, defragmentation::{DefragmentationMemo, DefragmentationMemoError}, - destination::{DestinationMemo, DestinationMemoError}, + destination::{compute_destination_memo, DestinationMemo, DestinationMemoError}, destination_with_payment_intent_id::DestinationWithPaymentIntentIdMemo, destination_with_payment_request_id::DestinationWithPaymentRequestIdMemo, gift_code_cancellation::GiftCodeCancellationMemo, @@ -106,18 +106,18 @@ pub enum MemoDecodingError { } impl_memo_enum! { MemoType, - AuthenticatedSender(AuthenticatedSenderMemo), - AuthenticatedSenderWithPaymentRequestId(AuthenticatedSenderWithPaymentRequestIdMemo), - AuthenticatedSenderWithPaymentIntentId(AuthenticatedSenderWithPaymentIntentIdMemo), - BurnRedemption(BurnRedemptionMemo), - Defragmentation(DefragmentationMemo), - Destination(DestinationMemo), - DestinationWithPaymentRequestId(DestinationWithPaymentRequestIdMemo), - DestinationWithPaymentIntentId(DestinationWithPaymentIntentIdMemo), - GiftCodeCancellation(GiftCodeCancellationMemo), - GiftCodeFunding(GiftCodeFundingMemo), - GiftCodeSender(GiftCodeSenderMemo), - Unused(UnusedMemo), + AuthenticatedSender(AuthenticatedSenderMemo), //[0x01, 0x00] + AuthenticatedSenderWithPaymentRequestId(AuthenticatedSenderWithPaymentRequestIdMemo), //[0x01, 0x01] + AuthenticatedSenderWithPaymentIntentId(AuthenticatedSenderWithPaymentIntentIdMemo), //[0x01, 0x02] + BurnRedemption(BurnRedemptionMemo), //[0x00, 0x01] + Defragmentation(DefragmentationMemo), //[0x00, 0x03] + Destination(DestinationMemo), //[0x02, 0x00] + DestinationWithPaymentRequestId(DestinationWithPaymentRequestIdMemo), //[0x02, 0x03] + DestinationWithPaymentIntentId(DestinationWithPaymentIntentIdMemo), //[0x02, 0x04] + GiftCodeCancellation(GiftCodeCancellationMemo), //[0x02, 0x02] + GiftCodeFunding(GiftCodeFundingMemo), //[0x02, 0x01] + GiftCodeSender(GiftCodeSenderMemo), //[0x00, 0x02] + Unused(UnusedMemo), //[0x00, 0x00] } #[cfg(test)]