2025-11-19 09:00:32 +02:00
|
|
|
use borsh::{BorshDeserialize, BorshSerialize};
|
2025-08-26 14:53:02 -03:00
|
|
|
use chacha20::{
|
|
|
|
|
ChaCha20,
|
|
|
|
|
cipher::{KeyIvInit, StreamCipher},
|
|
|
|
|
};
|
|
|
|
|
use risc0_zkvm::sha::{Impl, Sha256};
|
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "host")]
|
2025-09-12 16:00:57 +03:00
|
|
|
pub mod shared_key_derivation;
|
2025-08-26 14:53:02 -03:00
|
|
|
|
|
|
|
|
#[cfg(feature = "host")]
|
2026-01-21 17:27:23 -05:00
|
|
|
pub use shared_key_derivation::{EphemeralPublicKey, EphemeralSecretKey, ViewingPublicKey};
|
2025-08-26 14:53:02 -03:00
|
|
|
|
2025-08-27 16:24:20 -03:00
|
|
|
use crate::{Commitment, account::Account};
|
2025-08-26 14:53:02 -03:00
|
|
|
|
2025-09-17 08:59:14 +03:00
|
|
|
pub type Scalar = [u8; 32];
|
|
|
|
|
|
2025-12-16 14:05:34 +02:00
|
|
|
#[derive(Serialize, Deserialize, Clone, Copy)]
|
2025-09-12 16:00:57 +03:00
|
|
|
pub struct SharedSecretKey(pub [u8; 32]);
|
2025-08-26 14:53:02 -03:00
|
|
|
|
|
|
|
|
pub struct EncryptionScheme;
|
|
|
|
|
|
2025-11-19 09:00:32 +02:00
|
|
|
#[derive(Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
|
2026-02-27 16:37:28 +11:00
|
|
|
#[cfg_attr(any(feature = "host", test), derive(Clone, PartialEq, Eq))]
|
2025-08-26 14:53:02 -03:00
|
|
|
pub struct Ciphertext(pub(crate) Vec<u8>);
|
|
|
|
|
|
2026-02-27 16:37:28 +11:00
|
|
|
#[cfg(any(feature = "host", test))]
|
|
|
|
|
impl std::fmt::Debug for Ciphertext {
|
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
2026-03-03 23:21:08 +03:00
|
|
|
use std::fmt::Write as _;
|
|
|
|
|
|
|
|
|
|
let hex: String = self.0.iter().fold(String::new(), |mut acc, b| {
|
|
|
|
|
write!(acc, "{b:02x}").expect("writing to string should not fail");
|
|
|
|
|
acc
|
|
|
|
|
});
|
2026-02-27 16:37:28 +11:00
|
|
|
write!(f, "Ciphertext({hex})")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-26 14:53:02 -03:00
|
|
|
impl EncryptionScheme {
|
2026-03-03 23:21:08 +03:00
|
|
|
#[must_use]
|
2025-08-26 14:53:02 -03:00
|
|
|
pub fn encrypt(
|
|
|
|
|
account: &Account,
|
|
|
|
|
shared_secret: &SharedSecretKey,
|
|
|
|
|
commitment: &Commitment,
|
|
|
|
|
output_index: u32,
|
|
|
|
|
) -> Ciphertext {
|
2026-03-03 23:21:08 +03:00
|
|
|
let mut buffer = account.to_bytes().clone();
|
2025-08-26 14:53:02 -03:00
|
|
|
Self::symmetric_transform(&mut buffer, shared_secret, commitment, output_index);
|
|
|
|
|
Ciphertext(buffer)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn symmetric_transform(
|
|
|
|
|
buffer: &mut [u8],
|
|
|
|
|
shared_secret: &SharedSecretKey,
|
|
|
|
|
commitment: &Commitment,
|
|
|
|
|
output_index: u32,
|
|
|
|
|
) {
|
|
|
|
|
let key = Self::kdf(shared_secret, commitment, output_index);
|
|
|
|
|
let mut cipher = ChaCha20::new(&key.into(), &[0; 12].into());
|
|
|
|
|
cipher.apply_keystream(buffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn kdf(
|
|
|
|
|
shared_secret: &SharedSecretKey,
|
|
|
|
|
commitment: &Commitment,
|
|
|
|
|
output_index: u32,
|
|
|
|
|
) -> [u8; 32] {
|
|
|
|
|
let mut bytes = Vec::new();
|
|
|
|
|
|
2025-10-16 16:19:03 -03:00
|
|
|
bytes.extend_from_slice(b"NSSA/v0.2/KDF-SHA256/");
|
2025-08-26 14:53:02 -03:00
|
|
|
bytes.extend_from_slice(&shared_secret.0);
|
|
|
|
|
bytes.extend_from_slice(&commitment.to_byte_array());
|
|
|
|
|
bytes.extend_from_slice(&output_index.to_le_bytes());
|
|
|
|
|
|
|
|
|
|
Impl::hash_bytes(&bytes).as_bytes().try_into().unwrap()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "host")]
|
2026-03-03 23:21:08 +03:00
|
|
|
#[must_use]
|
2025-08-26 14:53:02 -03:00
|
|
|
pub fn decrypt(
|
|
|
|
|
ciphertext: &Ciphertext,
|
|
|
|
|
shared_secret: &SharedSecretKey,
|
|
|
|
|
commitment: &Commitment,
|
|
|
|
|
output_index: u32,
|
|
|
|
|
) -> Option<Account> {
|
|
|
|
|
use std::io::Cursor;
|
2026-03-03 23:21:08 +03:00
|
|
|
let mut buffer = ciphertext.0.clone();
|
2025-08-26 14:53:02 -03:00
|
|
|
Self::symmetric_transform(&mut buffer, shared_secret, commitment, output_index);
|
|
|
|
|
|
|
|
|
|
let mut cursor = Cursor::new(buffer.as_slice());
|
2026-03-05 12:35:18 +02:00
|
|
|
Account::from_cursor(&mut cursor)
|
|
|
|
|
.inspect_err(|err| {
|
|
|
|
|
println!(
|
|
|
|
|
"Failed to decode {ciphertext:?} \n
|
2026-03-03 23:21:08 +03:00
|
|
|
with secret {:?} ,\n
|
2026-03-05 12:35:18 +02:00
|
|
|
commitment {commitment:?} ,\n
|
|
|
|
|
and output_index {output_index} ,\n
|
|
|
|
|
with error {err:?}",
|
|
|
|
|
shared_secret.0
|
2026-03-03 23:21:08 +03:00
|
|
|
);
|
2026-03-05 12:35:18 +02:00
|
|
|
})
|
|
|
|
|
.ok()
|
2025-08-26 14:53:02 -03:00
|
|
|
}
|
|
|
|
|
}
|