Skip to content

Commit

Permalink
feat: unencrypted derivation index
Browse files Browse the repository at this point in the history
BREAKING CHANGE: removes derivation index ciphers
  • Loading branch information
grumbach authored and joshuef committed Sep 12, 2023
1 parent cd88f3f commit cc1f0c3
Show file tree
Hide file tree
Showing 6 changed files with 44 additions and 80 deletions.
8 changes: 5 additions & 3 deletions src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,19 @@ A user has a main key - `MainKey` - which is a `bls::SecretKey`.
It is in essense a key _pair_, as the corresponding `bls::PublicKey` can be gotten from that secret key.
The `bls::PublicKey`of that key pair, is the `PublicAddress` to which anyone can send tokens.

- A `Dbc` is a vehicle for transfering tokens.
- A `Dbc` is a container that holds value (counted in tokens).

- A `Dbc` has a unique identifier, `DbcId`, which is a `bls::PublicKey`.
The corresponding `bls::SecretKey` called `DerivedKey`, unlocks the value.

- A `Dbc` can only be fully spent. So you unlock it and take out all the tokens, and the `Dbc` is spent.

- A `Dbc` cannot be made public as it contains secrets, what the Network only ever sees is `SignedSpend`, which tells us which `Dbc` was spent

### Sending tokens:
When you send tokens to someone, you create a new `Dbc`, with a `DbcId` (a `bls::PublicKey`) by deriving it from the `PublicAddress` (a `bls::PublicKey`) of someone. You derive it using a random `DerivationIndex`, which you include (encrypted to the `PublicAddress`, which means only the corresponding `MainKey` can decrypt it) in the newly created `Dbc`.
When you send tokens to someone, you create a new `Dbc`, with a `DbcId` (a `bls::PublicKey`) by deriving it from the `PublicAddress` (a `bls::PublicKey`) of someone. You derive it using a random `DerivationIndex`, which you include in the newly created `Dbc`.
Also included in this new `Dbc` are the signatures of network nodes verifying that the input `Dbc(s)` that you emptied to create this new `Dbc`, are actually spent and was included in the transaction where this new `Dbc` was created. (The signatures part will change with the new network design.)
Since `Dbc`s contain secrets, they should be encrypted before being sent.

### Unknown connection between Dbcs and PublicAddresses:
Since the `DbcId` is derived from the `PublicAddress`, using a secret `DerivationIndex`, no one except sender and receiver knows that this new `Dbc` was sent to the `PublicAddress` of the receiver.
The recipient decrypts the `DerivationIndex` cipher, using their `MainKey` (remember, it's the corresponding `bls::SecretKey` of the `bls::PublicKey` in the `PublicAddress`).
4 changes: 2 additions & 2 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::{
transaction::{DbcTransaction, Output},
DbcId, DerivationIndex, DerivedKey, FeeOutput, Input, PublicAddress, Spend,
};
use crate::{Dbc, DbcCiphers, Error, Hash, Result, SignedSpend, Token};
use crate::{Dbc, DbcSecrets, Error, Hash, Result, SignedSpend, Token};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, BTreeSet};
Expand Down Expand Up @@ -237,7 +237,7 @@ impl DbcBuilder {
Dbc {
id: public_address.new_dbc_id(derivation_index),
src_tx: self.spent_tx.clone(),
ciphers: DbcCiphers::from((public_address, derivation_index)),
secrets: DbcSecrets::from((public_address, derivation_index)),
signed_spends: self.signed_spends.clone(),
},
output.token,
Expand Down
28 changes: 14 additions & 14 deletions src/dbc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
// permissions and limitations relating to use of the SAFE Network Software.

use crate::{
dbc_id::PublicAddress, transaction::DbcTransaction, DbcCiphers, DbcId, DerivationIndex,
dbc_id::PublicAddress, transaction::DbcTransaction, DbcId, DbcSecrets, DerivationIndex,
DerivedKey, Error, FeeOutput, Hash, MainKey, Result, SignedSpend, Token,
};
#[cfg(feature = "serde")]
Expand Down Expand Up @@ -63,9 +63,9 @@ pub struct Dbc {
/// The transaction where this DBC was created.
#[debug(skip)]
pub src_tx: DbcTransaction,
/// Encrypted information for and about the recipient of this Dbc.
/// Secret information for and about the recipient of this Dbc.
#[debug(skip)]
pub ciphers: DbcCiphers,
pub secrets: DbcSecrets,
/// The transaction's input's SignedSpends
pub signed_spends: BTreeSet<SignedSpend>,
}
Expand All @@ -78,7 +78,7 @@ impl Dbc {

// Return PublicAddress from which DbcId is derived.
pub fn public_address(&self) -> &PublicAddress {
&self.ciphers.public_address
&self.secrets.public_address
}

/// Return DerivedKey using MainKey supplied by caller.
Expand All @@ -88,12 +88,12 @@ impl Dbc {
if &main_key.public_address() != self.public_address() {
return Err(Error::MainKeyDoesNotMatchPublicAddress);
}
Ok(main_key.derive_key(&self.derivation_index(main_key)?))
Ok(main_key.derive_key(&self.derivation_index()))
}

/// Return the derivation index that was used to derive DbcId and corresponding DerivedKey of a Dbc.
pub fn derivation_index(&self, main_key: &MainKey) -> Result<DerivationIndex> {
self.ciphers.derivation_index(main_key)
pub fn derivation_index(&self) -> DerivationIndex {
self.secrets.derivation_index
}

/// Return the fee output used in the source transaction
Expand Down Expand Up @@ -126,7 +126,7 @@ impl Dbc {
pub fn hash(&self) -> Hash {
let mut sha3 = Sha3::v256();
sha3.update(self.src_tx.hash().as_ref());
sha3.update(&self.ciphers.to_bytes());
sha3.update(&self.secrets.to_bytes());

for sp in self.signed_spends.iter() {
sha3.update(&sp.to_bytes());
Expand Down Expand Up @@ -216,11 +216,11 @@ pub(crate) mod tests {
outputs: vec![Output::new(derived_key.dbc_id(), amount)],
fee: FeeOutput::new(Hash::default(), 3_500, Hash::default()),
};
let ciphers = DbcCiphers::from((&main_key.public_address(), &derivation_index));
let ciphers = DbcSecrets::from((&main_key.public_address(), &derivation_index));
let dbc = Dbc {
id: derived_key.dbc_id(),
src_tx: tx,
ciphers,
secrets: ciphers,
signed_spends: Default::default(),
};

Expand All @@ -247,11 +247,11 @@ pub(crate) mod tests {
outputs: vec![Output::new(derived_key.dbc_id(), amount)],
fee: FeeOutput::new(Hash::default(), 2_500, Hash::default()),
};
let ciphers = DbcCiphers::from((&main_key.public_address(), &derivation_index));
let ciphers = DbcSecrets::from((&main_key.public_address(), &derivation_index));
let dbc = Dbc {
id: derived_key.dbc_id(),
src_tx: tx,
ciphers,
secrets: ciphers,
signed_spends: Default::default(),
};

Expand Down Expand Up @@ -303,11 +303,11 @@ pub(crate) mod tests {
fee: FeeOutput::default(),
};

let ciphers = DbcCiphers::from((&main_key.public_address(), &derivation_index));
let ciphers = DbcSecrets::from((&main_key.public_address(), &derivation_index));
let dbc = Dbc {
id: derived_key.dbc_id(),
src_tx: tx,
ciphers,
secrets: ciphers,
signed_spends: Default::default(),
};

Expand Down
45 changes: 16 additions & 29 deletions src/dbc_ciphers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@
// KIND, either express or implied. Please review the Licences for the specific language governing
// permissions and limitations relating to use of the SAFE Network Software.

use crate::{dbc_id::PublicAddress, DerivationIndex, Hash, MainKey, Result};
use blsttc::Ciphertext;
use crate::{dbc_id::PublicAddress, DerivationIndex, Hash};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use tiny_keccak::{Hasher, Sha3};

#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct DbcCiphers {
pub struct DbcSecrets {
/// This is the PublicAddress to which tokens are send. The PublicAddress may be published
/// and multiple payments sent to this address by various parties. It is useful for
/// accepting donations, for example.
Expand All @@ -26,56 +25,44 @@ pub struct DbcCiphers {
/// and never seen by the spentbook nodes.
///
/// The DbcId used in the transaction is derived from this PublicAddress using a random
/// derivation index, which is stored (encrypted) in derivation_index_cipher.
/// derivation index, which is stored in derivation_index.
pub public_address: PublicAddress,

/// This indicates which index to use when deriving the DbcId of the
/// Dbc, from the PublicAddress.
///
/// This index is stored in encrypted form, and is encrypted to the PublicAddress.
/// So the actual PublicAddress the tokens in this Dbc was sent to, is unknown to
/// anyone not in posession of the MainKey corresponding to the above mentioned PublicAddress.
pub derivation_index_cipher: Ciphertext,
pub derivation_index: DerivationIndex,
}

/// Represents the ciphers of a Dbc.
impl From<(PublicAddress, Ciphertext)> for DbcCiphers {
// Create a new DbcCiphers for signing.
fn from(params: (PublicAddress, Ciphertext)) -> Self {
let (public_address, derivation_index_cipher) = params;
/// Represents the Secrets of a Dbc.
impl From<(PublicAddress, DerivationIndex)> for DbcSecrets {
// Create a new DbcSecrets for signing.
fn from(params: (PublicAddress, DerivationIndex)) -> Self {
let (public_address, derivation_index) = params;
Self {
public_address,
derivation_index_cipher,
derivation_index,
}
}
}

/// Represents the ciphers of a Dbc.
impl From<(&PublicAddress, &DerivationIndex)> for DbcCiphers {
// Create a new DbcCiphers for signing.
/// Represents the Secrets of a Dbc.
impl From<(&PublicAddress, &DerivationIndex)> for DbcSecrets {
// Create a new DbcSecrets for signing.
fn from(params: (&PublicAddress, &DerivationIndex)) -> Self {
let (public_address, derivation_index) = params;
let derivation_index_cipher = public_address.encrypt(derivation_index);

Self {
public_address: *public_address,
derivation_index_cipher,
derivation_index: *derivation_index,
}
}
}

impl DbcCiphers {
pub(crate) fn derivation_index(&self, key_source: &MainKey) -> Result<DerivationIndex> {
let bytes = key_source.decrypt_index(&self.derivation_index_cipher)?;
let mut idx = [0u8; 32];
idx.copy_from_slice(&bytes[0..32]);
Ok(idx)
}

impl DbcSecrets {
pub fn to_bytes(&self) -> Vec<u8> {
let mut bytes: Vec<u8> = Default::default();
bytes.extend(&self.public_address.to_bytes());
bytes.extend(&self.derivation_index_cipher.to_bytes());
bytes.extend(&self.derivation_index);
bytes
}

Expand Down
37 changes: 6 additions & 31 deletions src/dbc_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@

use crate::{
rand::{distributions::Standard, Rng, RngCore},
Error, PublicKey, Result,
PublicKey,
};
use blsttc::{serde_impl::SerdeSecret, Ciphertext, SecretKey, PK_SIZE};
use blsttc::{serde_impl::SerdeSecret, SecretKey, PK_SIZE};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

Expand Down Expand Up @@ -70,7 +70,7 @@ impl DerivedKey {
/// they generate the id of the Dbc - the DbcId - that shall hold the tokens.
/// The DbcId is generated from this PublicAddress, and only the sender
/// will at this point know that the DbcId is related to this PublicAddress.
/// When creating the Dbc using that DbcId, the sender will also encrypt the
/// When creating the Dbc using that DbcId, the sender will also include the
/// DerivationIndex that was used to generate the DbcId, so that the recipient behind
/// the PublicAddress can also see that the DbcId is related to this PublicAddress.
/// The recipient can then use the received DerivationIndex to generate the DerivedKey
Expand All @@ -97,11 +97,6 @@ impl PublicAddress {
DbcId(self.0.derive_child(index))
}

/// To send tokens to this address, a derivation index is encrypted
pub fn encrypt(&self, derivation_index: &DerivationIndex) -> Ciphertext {
self.0.encrypt(derivation_index)
}

pub fn to_bytes(self) -> [u8; PK_SIZE] {
self.0.to_bytes()
}
Expand Down Expand Up @@ -133,37 +128,17 @@ impl MainKey {
self.0.sign(msg)
}

/// When someone wants to send tokens to the PublicAddress of this MainKey,
/// they generate the id of the Dbc - the DbcId - that shall hold the tokens.
/// The created Dbc contains the encrypted derivation index, that is decrypted using
/// this MainKey instance.
/// The index is then used to derive the key - the DerivedKey - corresponding to the DbcId of the
/// Dbc sent to you. With that DerivedKey you will have access to the tokens in the Dbc.
pub fn decrypt_index(&self, derivation_index_cipher: &Ciphertext) -> Result<DerivationIndex> {
let bytes = self
.0
.decrypt(derivation_index_cipher)
.ok_or(Error::DecryptionBySecretKeyFailed)?;

let mut index = [0u8; 32];
index.copy_from_slice(&bytes[0..32]);

Ok(index)
}

/// Derive the key - the DerivedKey - corresponding to a DbcId
/// which was also derived using the same DerivationIndex.
///
/// When someone wants to send tokens to the PublicAddress of this MainKey,
/// they generate the id of the Dbc - the DbcId - that shall hold the tokens.
/// The recipient of the tokens, is the person/entity that holds this MainKey.
///
/// The created Dbc contains the _encrypted_ form of the derivation index that was used to
/// generate that very DbcId. The sender encrypted it so that no-one but the recipient of the
/// tokens in the Dbc (and the sender itself of course) shall be able to see which index was used.
/// This encrypted index is then decrypted by the recipient, using this MainKey instance (see `fn decrypt_index` above).
/// The created Dbc contains the derivation index that was used to
/// generate that very DbcId.
///
/// When passing the resulting decrypted derivation index to this function (`fn derive_key`),
/// When passing the derivation index to this function (`fn derive_key`),
/// a DerivedKey is generated corresponding to the DbcId. This DerivedKey can unlock the Dbc of that
/// DbcId, thus giving access to the tokens it holds.
/// By that, the recipient has received the tokens from the sender.
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub mod mock;
pub use crate::{
builder::{DbcBuilder, TransactionBuilder},
dbc::Dbc,
dbc_ciphers::DbcCiphers,
dbc_ciphers::DbcSecrets,
dbc_id::{random_derivation_index, DbcId, DerivationIndex, DerivedKey, MainKey, PublicAddress},
error::{Error, Result},
fee_output::FeeOutput,
Expand Down

0 comments on commit cc1f0c3

Please sign in to comment.