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

fix: hide sensitive data on tari repo (see issue #4846) #4967

Conversation

jorgeantonio21
Copy link
Contributor

Description

The goal of this PR is to zeroize sensitive data content across the Tari repo. We pay special attention to kdf key data and other occurrences of sensitive data. For more details, we refer to issue #4846. Moreover, this PR is a companion to #4953 and #4925

Motivation and Context

Tackle issue #4846.

How Has This Been Tested?

@jorgeantonio21 jorgeantonio21 marked this pull request as ready for review November 28, 2022 13:44
sdbondi
sdbondi previously approved these changes Nov 28, 2022
Copy link
Member

@sdbondi sdbondi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

I'm guessing there is still a leak at the clap level. Doesn't look like they support marking sensitive args.

applications/tari_console_wallet/src/lib.rs Outdated Show resolved Hide resolved
comms/core/src/noise/mod.rs Outdated Show resolved Hide resolved
sdbondi
sdbondi previously approved these changes Nov 29, 2022
SWvheerden
SWvheerden previously approved these changes Nov 29, 2022
Copy link
Collaborator

@SWvheerden SWvheerden left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK

sdbondi
sdbondi previously approved these changes Nov 30, 2022
stringhandler
stringhandler previously approved these changes Nov 30, 2022
@stringhandler stringhandler merged commit bcc47e1 into tari-project:development Nov 30, 2022

use super::{CoreTransactionAEADKey, AEAD_KEY_LEN};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might consider renaming to something that describes the intent more clearly, like ValueEncryptionKey. But that's just a nit.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in PR 4994.

Comment on lines -118 to 132
const AEAD_KEY_LENGTH: usize = 32; // The length in bytes of a ChaCha20-Poly1305 AEAD key
fn kdf_aead(shared_secret: &PrivateKey, commitment: &Commitment) -> CoreTransactionAEADKey {
let output = DomainSeparatedHasher::<Blake256, TransactionKdfDomain>::new_with_label("encrypted_value")
.chain(shared_secret.as_bytes())
.chain(commitment.as_bytes())
.finalize();

*Key::from_slice(&output.as_ref()[..AEAD_KEY_LENGTH])
let default_array = SafeArray::<u8, AEAD_KEY_LEN>::default();
let mut aead_key = CoreTransactionAEADKey::from(default_array);
aead_key.reveal_mut().copy_from_slice(&output.as_ref()[..AEAD_KEY_LEN]);

aead_key
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can simplify this by using finalize_into on the hasher to populate aead_key in place. Otherwise output sticks around in memory.

use digest::FixedOutput;

let mut aead_key = CoreTransactionAEADKey::from(SafeArray::default());
DomainSeparatedHasher::<Blake256, TransactionKdfDomain>::new_with_label("encrypted_value")
        .chain(shared_secret.as_bytes())
        .chain(commitment.as_bytes())
        .finalize_into(GenericArray::from_mut_slice(aead_key.reveal_mut()));

aead_key

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in PR 4994.

value_bytes.clone_from_slice(&decrypted_bytes[..8]);
Ok(u64::from_le_bytes(value_bytes).into())
}
}

// Generate a ChaCha20-Poly1305 key from an ECDH shared secret and commitment using Blake2b
fn kdf_aead(shared_secret: &PrivateKey, commitment: &Commitment) -> Key {
const AEAD_KEY_LENGTH: usize = 32; // The length in bytes of a ChaCha20-Poly1305 AEAD key
fn kdf_aead(shared_secret: &PrivateKey, commitment: &Commitment) -> CoreTransactionAEADKey {
let output = DomainSeparatedHasher::<Blake256, TransactionKdfDomain>::new_with_label("encrypted_value")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May wish to rename the label to be consistent with the reverse-domain notation used elsewhere.

@@ -1550,7 +1551,8 @@ pub unsafe extern "C" fn encrypted_value_as_bytes(
#[no_mangle]
pub unsafe extern "C" fn encrypted_value_destroy(encrypted_value: *mut TariEncryptedValue) {
if !encrypted_value.is_null() {
Box::from_raw(encrypted_value);
// zeroize the data content of encrypted_value, as to prevent memory leaks
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this functionality even necessary since the encrypted value is already included on chain?

Comment on lines +69 to 76
fn noise_kdf(shared_key: &CommsDHKE) -> CommsNoiseKey {
let hasher = DomainSeparatedHasher::<Blake256, CommsCoreHashDomain>::new_with_label("noise.dh");
Digest::finalize(hasher.chain(shared_key.as_bytes())).into()
let mut comms_noise_kdf = CommsNoiseKey::from([0u8; NOISE_KEY_LEN]);
comms_noise_kdf
.reveal_mut()
.copy_from_slice(hasher.chain(shared_key.as_bytes()).finalize().as_ref());
comms_noise_kdf
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can simplify this and avoid an in-memory copy of the KDF output using finalize_into.

use digest::FixedOutput;
use digest::generic_array::GenericArray;
use tari_utilities::safe_array::SafeArray;

let mut comms_noise_key = CommsNoiseKey::from(SafeArray::default());
DomainSeparatedHasher::<Blake256, CommsCoreHashDomain>::new_with_label("noise.dh")
        .chain(shared_key.as_bytes())
        .finalize_into(GenericArray::from_mut_slice(comms_noise_key.reveal_mut()));
comms_noise_key

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in PR 4994.


pub(crate) const NOISE_KEY_LEN: usize = 32;

hidden_type!(CommsNoiseKey, [u8; NOISE_KEY_LEN]);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recommend redefining this as a SafeArray.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sdbondi made a comment regarding the use of SafeArray, in this PR. I think his point was accurate, given the use of this CommsNoiseKey.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in PR 4994 but certainly open to discussion.

stringhandler pushed a commit that referenced this pull request Dec 5, 2022
Description
---
Updates the use of `CoreTransactionAEADKey` and `CommsNoiseKey` to be populated in place. Switches `CommsNoiseKey` to use a `SafeArray` under the hood.

Extends the work of [PR 4967](#4967).

Motivation and Context
---
Earlier work updates the use of keys for value encryption and Noise. Value encryption keys use a `SafeArray` type in a `Hidden` wrapper, and Noise keys use a `Hidden` array. However, in both cases, a copy of the hash output used to populate the keys is left in memory.

This work mitigates the problem. Because the hashing API now supports in-place output finalization via `finalize_into` and `finalize_into_reset`, we can populate keys directly by mutable reference.

It also renames `CoreTransactionAEADKey` to `EncryptedValueKey` for clarity, and switches `CommsNoiseKey` to be a `SafeArray` under the hood. There is [discussion](#4967 (comment)) on whether `CommsNoiseKey` needs to be a `SafeArray`; while the reasoning makes sense, I still think it's good practice to take advantage of the benefits of `SafeArray` for array-like key types unless there's a compelling performance reason otherwise. Discussion on this is welcome!

How Has This Been Tested?
---
Existing tests pass.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants