mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-01-02 13:23:10 +00:00
Merge branch 'Pravdyvy/key-protocol-update-private' into Pravdyvy/wallet-privacy-preserving-transactions
This commit is contained in:
commit
ae70baf5c4
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -2623,6 +2623,7 @@ dependencies = [
|
||||
"borsh",
|
||||
"hex",
|
||||
"hex-literal 1.0.0",
|
||||
"k256",
|
||||
"nssa-core",
|
||||
"program-methods",
|
||||
"rand 0.8.5",
|
||||
|
||||
@ -1,24 +1,30 @@
|
||||
use elliptic_curve::PrimeField;
|
||||
use k256::{AffinePoint, Scalar};
|
||||
use k256::Scalar;
|
||||
use log::info;
|
||||
use nssa_core::{
|
||||
NullifierPublicKey, SharedSecretKey,
|
||||
encryption::{EphemeralPublicKey, EphemeralSecretKey, IncomingViewingPublicKey},
|
||||
};
|
||||
use sha2::Digest;
|
||||
|
||||
use crate::key_management::secret_holders::OutgoingViewingSecretKey;
|
||||
|
||||
#[derive(Debug)]
|
||||
///Ephemeral secret key holder. Non-clonable as intended for one-time use. Produces ephemeral public keys. Can produce shared secret for sender.
|
||||
pub struct EphemeralKeyHolder {
|
||||
ephemeral_secret_key: Scalar,
|
||||
ephemeral_secret_key: EphemeralSecretKey,
|
||||
}
|
||||
|
||||
impl EphemeralKeyHolder {
|
||||
pub fn new(
|
||||
receiver_nullifier_public_key: [u8; 32],
|
||||
sender_outgoing_viewing_secret_key: Scalar,
|
||||
receiver_nullifier_public_key: NullifierPublicKey,
|
||||
sender_outgoing_viewing_secret_key: OutgoingViewingSecretKey,
|
||||
nonce: u64,
|
||||
) -> Self {
|
||||
let mut hasher = sha2::Sha256::new();
|
||||
hasher.update(receiver_nullifier_public_key);
|
||||
hasher.update(nonce.to_le_bytes());
|
||||
hasher.update([0; 192]);
|
||||
hasher.update([0; 24]);
|
||||
|
||||
let hash_recepient = hasher.finalize();
|
||||
|
||||
@ -31,15 +37,18 @@ impl EphemeralKeyHolder {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_ephemeral_public_key(&self) -> AffinePoint {
|
||||
(AffinePoint::GENERATOR * self.ephemeral_secret_key).into()
|
||||
pub fn generate_ephemeral_public_key(&self) -> EphemeralPublicKey {
|
||||
EphemeralPublicKey::from_scalar(self.ephemeral_secret_key)
|
||||
}
|
||||
|
||||
pub fn calculate_shared_secret_sender(
|
||||
&self,
|
||||
receiver_incoming_viewing_public_key: AffinePoint,
|
||||
) -> AffinePoint {
|
||||
(receiver_incoming_viewing_public_key * self.ephemeral_secret_key).into()
|
||||
receiver_incoming_viewing_public_key: IncomingViewingPublicKey,
|
||||
) -> SharedSecretKey {
|
||||
SharedSecretKey::new(
|
||||
&self.ephemeral_secret_key,
|
||||
&receiver_incoming_viewing_public_key,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn log(&self) {
|
||||
|
||||
@ -4,25 +4,26 @@ use elliptic_curve::group::GroupEncoding;
|
||||
use elliptic_curve::point::AffineCoordinates;
|
||||
use k256::AffinePoint;
|
||||
use log::info;
|
||||
use secret_holders::{PrivateKeyHolder, SeedHolder, TopSecretKeyHolder};
|
||||
use nssa_core::{
|
||||
NullifierPublicKey, SharedSecretKey,
|
||||
encryption::{EphemeralPublicKey, IncomingViewingPublicKey},
|
||||
};
|
||||
use secret_holders::{PrivateKeyHolder, SecretSpendingKey, SeedHolder};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{Digest, digest::FixedOutput};
|
||||
use types::{CipherText, Nonce};
|
||||
|
||||
use crate::key_protocol_core::PublicKey;
|
||||
pub type PublicAccountSigningKey = [u8; 32];
|
||||
|
||||
pub mod ephemeral_key_holder;
|
||||
pub mod secret_holders;
|
||||
pub mod types;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
///Entrypoint to key management
|
||||
pub struct KeyChain {
|
||||
top_secret_key_holder: TopSecretKeyHolder,
|
||||
secret_spending_key: SecretSpendingKey,
|
||||
pub private_key_holder: PrivateKeyHolder,
|
||||
pub nullifer_public_key: [u8; 32],
|
||||
pub incoming_viewing_public_key: PublicKey,
|
||||
pub nullifer_public_key: NullifierPublicKey,
|
||||
pub incoming_viewing_public_key: IncomingViewingPublicKey,
|
||||
}
|
||||
|
||||
impl KeyChain {
|
||||
@ -30,15 +31,15 @@ impl KeyChain {
|
||||
//Currently dropping SeedHolder at the end of initialization.
|
||||
//Now entirely sure if we need it in the future.
|
||||
let seed_holder = SeedHolder::new_os_random();
|
||||
let top_secret_key_holder = seed_holder.produce_top_secret_key_holder();
|
||||
let secret_spending_key = seed_holder.produce_top_secret_key_holder();
|
||||
|
||||
let private_key_holder = top_secret_key_holder.produce_private_key_holder();
|
||||
let private_key_holder = secret_spending_key.produce_private_key_holder();
|
||||
|
||||
let nullifer_public_key = private_key_holder.generate_nullifier_public_key();
|
||||
let incoming_viewing_public_key = private_key_holder.generate_incoming_viewing_public_key();
|
||||
|
||||
Self {
|
||||
top_secret_key_holder,
|
||||
secret_spending_key,
|
||||
private_key_holder,
|
||||
nullifer_public_key,
|
||||
incoming_viewing_public_key,
|
||||
@ -48,7 +49,7 @@ impl KeyChain {
|
||||
pub fn produce_user_address(&self) -> [u8; 32] {
|
||||
let mut hasher = sha2::Sha256::new();
|
||||
|
||||
hasher.update(self.nullifer_public_key);
|
||||
hasher.update(&self.nullifer_public_key);
|
||||
hasher.update(self.incoming_viewing_public_key.to_bytes());
|
||||
|
||||
<TreeHashType>::from(hasher.finalize_fixed())
|
||||
@ -56,33 +57,20 @@ impl KeyChain {
|
||||
|
||||
pub fn calculate_shared_secret_receiver(
|
||||
&self,
|
||||
ephemeral_public_key_sender: AffinePoint,
|
||||
) -> AffinePoint {
|
||||
(ephemeral_public_key_sender
|
||||
* self
|
||||
.top_secret_key_holder
|
||||
.generate_incloming_viewing_secret_key())
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn decrypt_data(
|
||||
&self,
|
||||
ephemeral_public_key_sender: AffinePoint,
|
||||
ciphertext: CipherText,
|
||||
nonce: Nonce,
|
||||
) -> Result<Vec<u8>, aes_gcm::Error> {
|
||||
let shared_secret = self.calculate_shared_secret_receiver(ephemeral_public_key_sender);
|
||||
let cipher = Aes256Gcm::new(&shared_secret.x());
|
||||
|
||||
cipher.decrypt(&nonce, ciphertext.as_slice())
|
||||
ephemeral_public_key_sender: EphemeralPublicKey,
|
||||
) -> SharedSecretKey {
|
||||
SharedSecretKey::new(
|
||||
&self
|
||||
.secret_spending_key
|
||||
.generate_incoming_viewing_secret_key(),
|
||||
&ephemeral_public_key_sender,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn log(&self) {
|
||||
info!(
|
||||
"Secret spending key is {:?}",
|
||||
hex::encode(
|
||||
serde_json::to_vec(&self.top_secret_key_holder.secret_spending_key).unwrap()
|
||||
),
|
||||
hex::encode(serde_json::to_vec(&self.secret_spending_key).unwrap()),
|
||||
);
|
||||
info!(
|
||||
"Nulifier secret key is {:?}",
|
||||
@ -113,17 +101,9 @@ impl KeyChain {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use aes_gcm::{
|
||||
Aes256Gcm,
|
||||
aead::{Aead, KeyInit, OsRng},
|
||||
};
|
||||
use aes_gcm::aead::OsRng;
|
||||
use elliptic_curve::ff::Field;
|
||||
use elliptic_curve::group::prime::PrimeCurveAffine;
|
||||
use elliptic_curve::point::AffineCoordinates;
|
||||
use k256::{AffinePoint, ProjectivePoint, Scalar};
|
||||
use types::{CipherText, Nonce};
|
||||
|
||||
use crate::key_management::ephemeral_key_holder::EphemeralKeyHolder;
|
||||
use k256::{AffinePoint, Scalar};
|
||||
|
||||
use super::*;
|
||||
|
||||
@ -133,10 +113,7 @@ mod tests {
|
||||
let address_key_holder = KeyChain::new_os_random();
|
||||
|
||||
// Check that key holder fields are initialized with expected types
|
||||
assert_ne!(address_key_holder.nullifer_public_key, [0u8; 32]);
|
||||
assert!(!Into::<bool>::into(
|
||||
address_key_holder.incoming_viewing_public_key.is_identity()
|
||||
));
|
||||
assert_ne!(address_key_holder.nullifer_public_key.as_ref(), &[0u8; 32]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -145,171 +122,11 @@ mod tests {
|
||||
|
||||
// Generate a random ephemeral public key sender
|
||||
let scalar = Scalar::random(&mut OsRng);
|
||||
let ephemeral_public_key_sender = (ProjectivePoint::GENERATOR * scalar).to_affine();
|
||||
let ephemeral_public_key_sender = EphemeralPublicKey::from_scalar(scalar);
|
||||
|
||||
// Calculate shared secret
|
||||
let shared_secret =
|
||||
let _shared_secret =
|
||||
address_key_holder.calculate_shared_secret_receiver(ephemeral_public_key_sender);
|
||||
|
||||
// Ensure the shared secret is not an identity point (suggesting non-zero output)
|
||||
assert!(!Into::<bool>::into(shared_secret.is_identity()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decrypt_data() {
|
||||
let address_key_holder = KeyChain::new_os_random();
|
||||
|
||||
let test_receiver_nullifier_public_key = [42; 32];
|
||||
let sender_outgoing_viewing_key = address_key_holder
|
||||
.top_secret_key_holder
|
||||
.generate_outgoing_viewing_secret_key();
|
||||
let nonce = 0;
|
||||
|
||||
// Generate an ephemeral key and shared secret
|
||||
let ephemeral_public_key_sender = EphemeralKeyHolder::new(
|
||||
test_receiver_nullifier_public_key,
|
||||
sender_outgoing_viewing_key,
|
||||
nonce,
|
||||
)
|
||||
.generate_ephemeral_public_key();
|
||||
let shared_secret =
|
||||
address_key_holder.calculate_shared_secret_receiver(ephemeral_public_key_sender);
|
||||
|
||||
// Encrypt sample data
|
||||
let cipher = Aes256Gcm::new(&shared_secret.x());
|
||||
let nonce = Nonce::from_slice(b"unique nonce");
|
||||
let plaintext = b"Sensitive data";
|
||||
let ciphertext = cipher
|
||||
.encrypt(nonce, plaintext.as_ref())
|
||||
.expect("encryption failure");
|
||||
|
||||
// Attempt decryption
|
||||
let decrypted_data: Vec<u8> = address_key_holder
|
||||
.decrypt_data(
|
||||
ephemeral_public_key_sender,
|
||||
CipherText::from(ciphertext),
|
||||
*nonce,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Verify decryption is successful and matches original plaintext
|
||||
assert_eq!(decrypted_data, plaintext);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_calculate_shared_secret_with_identity_point() {
|
||||
let address_key_holder = KeyChain::new_os_random();
|
||||
|
||||
// Use identity point as ephemeral public key
|
||||
let identity_point = AffinePoint::identity();
|
||||
|
||||
// Calculate shared secret
|
||||
let shared_secret = address_key_holder.calculate_shared_secret_receiver(identity_point);
|
||||
|
||||
// The shared secret with the identity point should also result in the identity point
|
||||
assert!(Into::<bool>::into(shared_secret.is_identity()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_decrypt_data_with_incorrect_nonce() {
|
||||
let address_key_holder = KeyChain::new_os_random();
|
||||
|
||||
// Generate ephemeral public key and shared secret
|
||||
let scalar = Scalar::random(OsRng);
|
||||
let ephemeral_public_key_sender = (ProjectivePoint::GENERATOR * scalar).to_affine();
|
||||
let shared_secret =
|
||||
address_key_holder.calculate_shared_secret_receiver(ephemeral_public_key_sender);
|
||||
|
||||
// Encrypt sample data with a specific nonce
|
||||
let cipher = Aes256Gcm::new(&shared_secret.x());
|
||||
let nonce = Nonce::from_slice(b"unique nonce");
|
||||
let plaintext = b"Sensitive data";
|
||||
let ciphertext = cipher
|
||||
.encrypt(nonce, plaintext.as_ref())
|
||||
.expect("encryption failure");
|
||||
|
||||
// Attempt decryption with an incorrect nonce
|
||||
let incorrect_nonce = Nonce::from_slice(b"wrong nonce");
|
||||
let decrypted_data = address_key_holder
|
||||
.decrypt_data(
|
||||
ephemeral_public_key_sender,
|
||||
CipherText::from(ciphertext.clone()),
|
||||
*incorrect_nonce,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// The decryption should fail or produce incorrect output due to nonce mismatch
|
||||
assert_ne!(decrypted_data, plaintext);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_decrypt_data_with_incorrect_ciphertext() {
|
||||
let address_key_holder = KeyChain::new_os_random();
|
||||
|
||||
// Generate ephemeral public key and shared secret
|
||||
let scalar = Scalar::random(OsRng);
|
||||
let ephemeral_public_key_sender = (ProjectivePoint::GENERATOR * scalar).to_affine();
|
||||
let shared_secret =
|
||||
address_key_holder.calculate_shared_secret_receiver(ephemeral_public_key_sender);
|
||||
|
||||
// Encrypt sample data
|
||||
let cipher = Aes256Gcm::new(&shared_secret.x());
|
||||
let nonce = Nonce::from_slice(b"unique nonce");
|
||||
let plaintext = b"Sensitive data";
|
||||
let ciphertext = cipher
|
||||
.encrypt(nonce, plaintext.as_ref())
|
||||
.expect("encryption failure");
|
||||
|
||||
// Tamper with the ciphertext to simulate corruption
|
||||
let mut corrupted_ciphertext = ciphertext.clone();
|
||||
corrupted_ciphertext[0] ^= 1; // Flip a bit in the ciphertext
|
||||
|
||||
// Attempt decryption
|
||||
let result = address_key_holder
|
||||
.decrypt_data(
|
||||
ephemeral_public_key_sender,
|
||||
CipherText::from(corrupted_ciphertext),
|
||||
*nonce,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// The decryption should fail or produce incorrect output due to tampered ciphertext
|
||||
assert_ne!(result, plaintext);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encryption_decryption_round_trip() {
|
||||
let address_key_holder = KeyChain::new_os_random();
|
||||
|
||||
// Generate ephemeral key and shared secret
|
||||
let scalar = Scalar::random(OsRng);
|
||||
let ephemeral_public_key_sender = (ProjectivePoint::GENERATOR * scalar).to_affine();
|
||||
|
||||
// Encrypt sample data
|
||||
let plaintext = b"Round-trip test data";
|
||||
let nonce = Nonce::from_slice(b"unique nonce");
|
||||
|
||||
let shared_secret =
|
||||
address_key_holder.calculate_shared_secret_receiver(ephemeral_public_key_sender);
|
||||
let cipher = Aes256Gcm::new(&shared_secret.x());
|
||||
|
||||
let ciphertext = cipher
|
||||
.encrypt(nonce, plaintext.as_ref())
|
||||
.expect("encryption failure");
|
||||
|
||||
// Decrypt the data using the `KeyChain` instance
|
||||
let decrypted_data = address_key_holder
|
||||
.decrypt_data(
|
||||
ephemeral_public_key_sender,
|
||||
CipherText::from(ciphertext),
|
||||
*nonce,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Verify the decrypted data matches the original plaintext
|
||||
assert_eq!(decrypted_data, plaintext);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
use bip39::Mnemonic;
|
||||
use common::TreeHashType;
|
||||
use elliptic_curve::PrimeField;
|
||||
use k256::{AffinePoint, Scalar};
|
||||
use k256::Scalar;
|
||||
use nssa_core::{NullifierPublicKey, NullifierSecretKey, encryption::IncomingViewingPublicKey};
|
||||
use rand::{RngCore, rngs::OsRng};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{Digest, digest::FixedOutput};
|
||||
@ -15,17 +16,18 @@ pub struct SeedHolder {
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
///Secret spending key holder. Produces `PrivateKeyHolder` objects.
|
||||
pub struct TopSecretKeyHolder {
|
||||
pub(crate) secret_spending_key: [u8; 32],
|
||||
}
|
||||
///Secret spending key object. Can produce `PrivateKeyHolder` objects.
|
||||
pub struct SecretSpendingKey(pub(crate) [u8; 32]);
|
||||
|
||||
pub type IncomingViewingSecretKey = Scalar;
|
||||
pub type OutgoingViewingSecretKey = Scalar;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
///Private key holder. Produces public keys. Can produce address. Can produce shared secret for recepient.
|
||||
pub struct PrivateKeyHolder {
|
||||
pub nullifier_secret_key: [u8; 32],
|
||||
pub(crate) incoming_viewing_secret_key: Scalar,
|
||||
pub outgoing_viewing_secret_key: Scalar,
|
||||
pub(crate) nullifier_secret_key: NullifierSecretKey,
|
||||
pub(crate) incoming_viewing_secret_key: IncomingViewingSecretKey,
|
||||
pub(crate) outgoing_viewing_secret_key: OutgoingViewingSecretKey,
|
||||
}
|
||||
|
||||
impl SeedHolder {
|
||||
@ -52,74 +54,65 @@ impl SeedHolder {
|
||||
*hash.first_chunk::<32>().unwrap()
|
||||
}
|
||||
|
||||
pub fn produce_top_secret_key_holder(&self) -> TopSecretKeyHolder {
|
||||
TopSecretKeyHolder {
|
||||
secret_spending_key: self.generate_secret_spending_key_hash(),
|
||||
}
|
||||
pub fn produce_top_secret_key_holder(&self) -> SecretSpendingKey {
|
||||
SecretSpendingKey(self.generate_secret_spending_key_hash())
|
||||
}
|
||||
}
|
||||
|
||||
impl TopSecretKeyHolder {
|
||||
pub fn generate_nullifier_secret_key(&self) -> [u8; 32] {
|
||||
impl SecretSpendingKey {
|
||||
pub fn generate_nullifier_secret_key(&self) -> NullifierSecretKey {
|
||||
let mut hasher = sha2::Sha256::new();
|
||||
|
||||
hasher.update("NSSA_keys");
|
||||
hasher.update(self.secret_spending_key);
|
||||
hasher.update(self.0);
|
||||
hasher.update([1u8]);
|
||||
hasher.update([0u8; 176]);
|
||||
hasher.update([0u8; 22]);
|
||||
|
||||
<TreeHashType>::from(hasher.finalize_fixed())
|
||||
<NullifierSecretKey>::from(hasher.finalize_fixed())
|
||||
}
|
||||
|
||||
pub fn generate_incloming_viewing_secret_key(&self) -> Scalar {
|
||||
pub fn generate_incoming_viewing_secret_key(&self) -> IncomingViewingSecretKey {
|
||||
let mut hasher = sha2::Sha256::new();
|
||||
|
||||
hasher.update("NSSA_keys");
|
||||
hasher.update(self.secret_spending_key);
|
||||
hasher.update(self.0);
|
||||
hasher.update([2u8]);
|
||||
hasher.update([0u8; 176]);
|
||||
hasher.update([0u8; 22]);
|
||||
|
||||
let hash = <TreeHashType>::from(hasher.finalize_fixed());
|
||||
|
||||
Scalar::from_repr(hash.into()).unwrap()
|
||||
IncomingViewingSecretKey::from_repr(hash.into()).unwrap()
|
||||
}
|
||||
|
||||
pub fn generate_outgoing_viewing_secret_key(&self) -> Scalar {
|
||||
pub fn generate_outgoing_viewing_secret_key(&self) -> OutgoingViewingSecretKey {
|
||||
let mut hasher = sha2::Sha256::new();
|
||||
|
||||
hasher.update("NSSA_keys");
|
||||
hasher.update(self.secret_spending_key);
|
||||
hasher.update(self.0);
|
||||
hasher.update([3u8]);
|
||||
hasher.update([0u8; 176]);
|
||||
hasher.update([0u8; 22]);
|
||||
|
||||
let hash = <TreeHashType>::from(hasher.finalize_fixed());
|
||||
|
||||
Scalar::from_repr(hash.into()).unwrap()
|
||||
OutgoingViewingSecretKey::from_repr(hash.into()).unwrap()
|
||||
}
|
||||
|
||||
pub fn produce_private_key_holder(&self) -> PrivateKeyHolder {
|
||||
PrivateKeyHolder {
|
||||
nullifier_secret_key: self.generate_nullifier_secret_key(),
|
||||
incoming_viewing_secret_key: self.generate_incloming_viewing_secret_key(),
|
||||
incoming_viewing_secret_key: self.generate_incoming_viewing_secret_key(),
|
||||
outgoing_viewing_secret_key: self.generate_outgoing_viewing_secret_key(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PrivateKeyHolder {
|
||||
pub fn generate_nullifier_public_key(&self) -> [u8; 32] {
|
||||
let mut hasher = sha2::Sha256::new();
|
||||
|
||||
hasher.update("NSSA_keys");
|
||||
hasher.update(self.nullifier_secret_key);
|
||||
hasher.update([7u8]);
|
||||
hasher.update([0u8; 176]);
|
||||
|
||||
<TreeHashType>::from(hasher.finalize_fixed())
|
||||
pub fn generate_nullifier_public_key(&self) -> NullifierPublicKey {
|
||||
(&self.nullifier_secret_key).into()
|
||||
}
|
||||
|
||||
pub fn generate_incoming_viewing_public_key(&self) -> AffinePoint {
|
||||
(AffinePoint::GENERATOR * self.incoming_viewing_secret_key).into()
|
||||
pub fn generate_incoming_viewing_public_key(&self) -> IncomingViewingPublicKey {
|
||||
IncomingViewingPublicKey::from_scalar(self.incoming_viewing_secret_key)
|
||||
}
|
||||
}
|
||||
|
||||
@ -151,7 +144,7 @@ mod tests {
|
||||
|
||||
let top_secret_key_holder = seed_holder.produce_top_secret_key_holder();
|
||||
|
||||
let _ = top_secret_key_holder.generate_incloming_viewing_secret_key();
|
||||
let _ = top_secret_key_holder.generate_incoming_viewing_secret_key();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
use elliptic_curve::{
|
||||
consts::{B0, B1},
|
||||
generic_array::GenericArray,
|
||||
};
|
||||
use sha2::digest::typenum::{UInt, UTerm};
|
||||
|
||||
pub type CipherText = Vec<u8>;
|
||||
pub type Nonce = GenericArray<u8, UInt<UInt<UInt<UInt<UTerm, B1>, B1>, B0>, B0>>;
|
||||
@ -14,6 +14,7 @@ secp256k1 = "0.31.1"
|
||||
rand = "0.8"
|
||||
borsh = "1.5.7"
|
||||
hex = "0.4.3"
|
||||
k256 = "0.13.3"
|
||||
|
||||
[dev-dependencies]
|
||||
test-program-methods = { path = "test_program_methods" }
|
||||
|
||||
@ -14,7 +14,7 @@ use crate::SharedSecretKey;
|
||||
pub struct Secp256k1Point(pub Vec<u8>);
|
||||
|
||||
impl Secp256k1Point {
|
||||
pub fn from_scalar(value: [u8; 32]) -> Secp256k1Point {
|
||||
pub fn from_scalar(value: Scalar) -> Secp256k1Point {
|
||||
let x_bytes: FieldBytes = value.into();
|
||||
let x = Scalar::from_repr(x_bytes).unwrap();
|
||||
|
||||
@ -26,7 +26,7 @@ impl Secp256k1Point {
|
||||
}
|
||||
}
|
||||
|
||||
pub type EphemeralSecretKey = [u8; 32];
|
||||
pub type EphemeralSecretKey = Scalar;
|
||||
pub type EphemeralPublicKey = Secp256k1Point;
|
||||
pub type IncomingViewingPublicKey = Secp256k1Point;
|
||||
impl From<&EphemeralSecretKey> for EphemeralPublicKey {
|
||||
@ -36,8 +36,7 @@ impl From<&EphemeralSecretKey> for EphemeralPublicKey {
|
||||
}
|
||||
|
||||
impl SharedSecretKey {
|
||||
pub fn new(scalar: &[u8; 32], point: &Secp256k1Point) -> Self {
|
||||
let scalar = Scalar::from_repr((*scalar).into()).unwrap();
|
||||
pub fn new(scalar: &Scalar, point: &Secp256k1Point) -> Self {
|
||||
let point: [u8; 33] = point.0.clone().try_into().unwrap();
|
||||
|
||||
let encoded = EncodedPoint::from_bytes(point).unwrap();
|
||||
|
||||
@ -7,6 +7,12 @@ use crate::Commitment;
|
||||
#[cfg_attr(any(feature = "host", test), derive(Debug, Clone, Hash))]
|
||||
pub struct NullifierPublicKey(pub [u8; 32]);
|
||||
|
||||
impl AsRef<[u8]> for NullifierPublicKey {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.0.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&NullifierSecretKey> for NullifierPublicKey {
|
||||
fn from(value: &NullifierSecretKey) -> Self {
|
||||
let mut bytes = Vec::new();
|
||||
|
||||
@ -224,3 +224,34 @@ impl PrivacyPreservingTransaction {
|
||||
Ok(PrivacyPreservingTransaction::new(message, witness_set))
|
||||
}
|
||||
}
|
||||
|
||||
impl Proof {
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = Vec::new();
|
||||
let proof_len = self.0.len() as u32;
|
||||
bytes.extend_from_slice(&proof_len.to_le_bytes());
|
||||
bytes.extend_from_slice(&self.0);
|
||||
bytes
|
||||
}
|
||||
|
||||
pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result<Self, NssaError> {
|
||||
let proof_len = u32_from_cursor(cursor) as usize;
|
||||
let mut proof = Vec::with_capacity(proof_len);
|
||||
|
||||
for _ in 0..proof_len {
|
||||
let mut one_byte_buf = [0u8];
|
||||
|
||||
cursor.read_exact(&mut one_byte_buf)?;
|
||||
|
||||
proof.push(one_byte_buf[0]);
|
||||
}
|
||||
Ok(Self(proof))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Improve error handling. Remove unwraps.
|
||||
pub fn u32_from_cursor(cursor: &mut Cursor<&[u8]>) -> u32 {
|
||||
let mut word_buf = [0u8; 4];
|
||||
cursor.read_exact(&mut word_buf).unwrap();
|
||||
u32::from_le_bytes(word_buf)
|
||||
}
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
use std::io::{Cursor, Read};
|
||||
|
||||
use nssa_core::{
|
||||
MembershipProof, NullifierPublicKey, NullifierSecretKey, PrivacyPreservingCircuitInput,
|
||||
PrivacyPreservingCircuitOutput, SharedSecretKey,
|
||||
@ -14,44 +12,7 @@ use program_methods::{PRIVACY_PRESERVING_CIRCUIT_ELF, PRIVACY_PRESERVING_CIRCUIT
|
||||
|
||||
/// Proof of the privacy preserving execution circuit
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Proof(pub(super) Vec<u8>);
|
||||
|
||||
impl Proof {
|
||||
pub(crate) fn is_valid_for(&self, circuit_output: &PrivacyPreservingCircuitOutput) -> bool {
|
||||
let inner: InnerReceipt = borsh::from_slice(&self.0).unwrap();
|
||||
let receipt = Receipt::new(inner, circuit_output.to_bytes());
|
||||
receipt.verify(PRIVACY_PRESERVING_CIRCUIT_ID).is_ok()
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = Vec::new();
|
||||
let proof_len = self.0.len() as u32;
|
||||
bytes.extend_from_slice(&proof_len.to_le_bytes());
|
||||
bytes.extend_from_slice(&self.0);
|
||||
bytes
|
||||
}
|
||||
|
||||
pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result<Self, NssaError> {
|
||||
let proof_len = u32_from_cursor(cursor) as usize;
|
||||
let mut proof = Vec::with_capacity(proof_len);
|
||||
|
||||
for _ in 0..proof_len {
|
||||
let mut one_byte_buf = [0u8];
|
||||
|
||||
cursor.read_exact(&mut one_byte_buf)?;
|
||||
|
||||
proof.push(one_byte_buf[0]);
|
||||
}
|
||||
Ok(Self(proof))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Improve error handling. Remove unwraps.
|
||||
pub fn u32_from_cursor(cursor: &mut Cursor<&[u8]>) -> u32 {
|
||||
let mut word_buf = [0u8; 4];
|
||||
cursor.read_exact(&mut word_buf).unwrap();
|
||||
u32::from_le_bytes(word_buf)
|
||||
}
|
||||
pub struct Proof(pub(crate) Vec<u8>);
|
||||
|
||||
/// Generates a proof of the execution of a NSSA program inside the privacy preserving execution
|
||||
/// circuit
|
||||
@ -117,8 +78,17 @@ fn execute_and_prove_program(
|
||||
.receipt)
|
||||
}
|
||||
|
||||
impl Proof {
|
||||
pub(crate) fn is_valid_for(&self, circuit_output: &PrivacyPreservingCircuitOutput) -> bool {
|
||||
let inner: InnerReceipt = borsh::from_slice(&self.0).unwrap();
|
||||
let receipt = Receipt::new(inner, circuit_output.to_bytes());
|
||||
receipt.verify(PRIVACY_PRESERVING_CIRCUIT_ID).is_ok()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use k256::{Scalar, elliptic_curve::PrimeField};
|
||||
use nssa_core::{
|
||||
Commitment, EncryptionScheme, Nullifier,
|
||||
account::{Account, AccountWithMetadata},
|
||||
@ -170,7 +140,7 @@ mod tests {
|
||||
let expected_sender_pre = sender.clone();
|
||||
let recipient_keys = test_private_account_keys_1();
|
||||
|
||||
let esk = [3; 32];
|
||||
let esk = Scalar::from_repr([3; 32].into()).unwrap();
|
||||
let shared_secret = SharedSecretKey::new(&esk, &recipient_keys.ivk());
|
||||
|
||||
let (output, proof) = execute_and_prove(
|
||||
@ -251,10 +221,10 @@ mod tests {
|
||||
Commitment::new(&recipient_keys.npk(), &expected_private_account_2),
|
||||
];
|
||||
|
||||
let esk_1 = [3; 32];
|
||||
let esk_1 = Scalar::from_repr([3; 32].into()).unwrap();
|
||||
let shared_secret_1 = SharedSecretKey::new(&esk_1, &sender_keys.ivk());
|
||||
|
||||
let esk_2 = [5; 32];
|
||||
let esk_2 = Scalar::from_repr([5; 32].into()).unwrap();
|
||||
let shared_secret_2 = SharedSecretKey::new(&esk_2, &recipient_keys.ivk());
|
||||
|
||||
let (output, proof) = execute_and_prove(
|
||||
|
||||
@ -90,6 +90,7 @@ impl Message {
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use k256::{Scalar, elliptic_curve::PrimeField};
|
||||
use std::io::Cursor;
|
||||
|
||||
use nssa_core::{
|
||||
@ -151,10 +152,10 @@ pub mod tests {
|
||||
#[test]
|
||||
fn test_encrypted_account_data_constructor() {
|
||||
let npk = NullifierPublicKey::from(&[1; 32]);
|
||||
let ivk = IncomingViewingPublicKey::from(&[2; 32]);
|
||||
let ivk = IncomingViewingPublicKey::from(&Scalar::from_repr([2; 32].into()).unwrap());
|
||||
let account = Account::default();
|
||||
let commitment = Commitment::new(&npk, &account);
|
||||
let esk = [3; 32];
|
||||
let esk = Scalar::from_repr([3; 32].into()).unwrap();
|
||||
let shared_secret = SharedSecretKey::new(&esk, &ivk);
|
||||
let epk = EphemeralPublicKey::from_scalar(esk);
|
||||
let ciphertext = EncryptionScheme::encrypt(&account, &shared_secret, &commitment, 2);
|
||||
|
||||
@ -227,6 +227,7 @@ pub mod tests {
|
||||
signature::PrivateKey,
|
||||
};
|
||||
|
||||
use k256::{Scalar, elliptic_curve::PrimeField};
|
||||
use nssa_core::{
|
||||
Commitment, Nullifier, NullifierPublicKey, NullifierSecretKey, SharedSecretKey,
|
||||
account::{Account, AccountWithMetadata, Nonce},
|
||||
@ -743,7 +744,7 @@ pub mod tests {
|
||||
|
||||
pub struct TestPrivateKeys {
|
||||
pub nsk: NullifierSecretKey,
|
||||
pub isk: [u8; 32],
|
||||
pub isk: Scalar,
|
||||
}
|
||||
|
||||
impl TestPrivateKeys {
|
||||
@ -759,14 +760,14 @@ pub mod tests {
|
||||
pub fn test_private_account_keys_1() -> TestPrivateKeys {
|
||||
TestPrivateKeys {
|
||||
nsk: [13; 32],
|
||||
isk: [31; 32],
|
||||
isk: Scalar::from_repr([31; 32].into()).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn test_private_account_keys_2() -> TestPrivateKeys {
|
||||
TestPrivateKeys {
|
||||
nsk: [38; 32],
|
||||
isk: [83; 32],
|
||||
isk: Scalar::from_repr([83; 32].into()).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -788,7 +789,7 @@ pub mod tests {
|
||||
is_authorized: false,
|
||||
};
|
||||
|
||||
let esk = [3; 32];
|
||||
let esk = Scalar::from_repr([3; 32].into()).unwrap();
|
||||
let shared_secret = SharedSecretKey::new(&esk, &recipient_keys.ivk());
|
||||
let epk = EphemeralPublicKey::from_scalar(esk);
|
||||
|
||||
@ -834,11 +835,11 @@ pub mod tests {
|
||||
is_authorized: false,
|
||||
};
|
||||
|
||||
let esk_1 = [3; 32];
|
||||
let esk_1 = Scalar::from_repr([3; 32].into()).unwrap();
|
||||
let shared_secret_1 = SharedSecretKey::new(&esk_1, &sender_keys.ivk());
|
||||
let epk_1 = EphemeralPublicKey::from_scalar(esk_1);
|
||||
|
||||
let esk_2 = [3; 32];
|
||||
let esk_2 = Scalar::from_repr([3; 32].into()).unwrap();
|
||||
let shared_secret_2 = SharedSecretKey::new(&esk_2, &recipient_keys.ivk());
|
||||
let epk_2 = EphemeralPublicKey::from_scalar(esk_2);
|
||||
|
||||
@ -894,7 +895,7 @@ pub mod tests {
|
||||
is_authorized: false,
|
||||
};
|
||||
|
||||
let esk = [3; 32];
|
||||
let esk = Scalar::from_repr([3; 32].into()).unwrap();
|
||||
let shared_secret = SharedSecretKey::new(&esk, &sender_keys.ivk());
|
||||
let epk = EphemeralPublicKey::from_scalar(esk);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user