Skip to content

Commit

Permalink
nonce comes from caller
Browse files Browse the repository at this point in the history
  • Loading branch information
petarjuki7 committed Nov 21, 2024
1 parent efbdf51 commit e201ffb
Show file tree
Hide file tree
Showing 8 changed files with 170 additions and 102 deletions.
2 changes: 1 addition & 1 deletion crates/crypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ license.workspace = true
curve25519-dalek.workspace = true
ed25519-dalek = { workspace = true, features = ["rand_core"] }
ring.workspace = true
rand.workspace = true

calimero-primitives = { path = "../primitives", features = ["rand"] }

[dev-dependencies]
eyre.workspace = true
rand.workspace = true

[lints]
workspace = true
51 changes: 22 additions & 29 deletions crates/crypto/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,43 +1,33 @@
use calimero_primitives::identity::{PrivateKey, PublicKey};
use ed25519_dalek::{SecretKey, SigningKey};
use rand::{thread_rng, Rng};
use ring::aead;

pub const NONCE_LEN: usize = 12;

pub type Nonce = [u8; NONCE_LEN];

#[derive(Copy, Clone, Debug)]
pub struct SharedKey {
key: SecretKey,
}

#[derive(Debug)]
pub struct Record {
pub token: Vec<u8>,
pub nonce: [u8; NONCE_LEN],
}

impl SharedKey {
pub fn new(sk: &PrivateKey, pk: &PublicKey) -> (Self, [u8; NONCE_LEN]) {
let nonce = thread_rng().gen::<[u8; NONCE_LEN]>();
(
SharedKey {
key: (SigningKey::from_bytes(sk).to_scalar()
* curve25519_dalek::edwards::CompressedEdwardsY(**pk)
.decompress()
.expect("pk should be guaranteed to be the y coordinate"))
.compress()
.to_bytes(),
},
nonce,
)
pub fn new(sk: &PrivateKey, pk: &PublicKey) -> Self {
SharedKey {
key: (SigningKey::from_bytes(sk).to_scalar()
* curve25519_dalek::edwards::CompressedEdwardsY(**pk)
.decompress()
.expect("pk should be guaranteed to be the y coordinate"))
.compress()
.to_bytes(),
}
}

pub fn from_sk(sk: &PrivateKey) -> (Self, [u8; NONCE_LEN]) {
let nonce = thread_rng().gen::<[u8; NONCE_LEN]>();
(SharedKey { key: **sk }, nonce)
pub fn from_sk(sk: &PrivateKey) -> Self {
SharedKey { key: **sk }
}

pub fn encrypt(&self, payload: Vec<u8>, nonce: [u8; NONCE_LEN]) -> Option<Vec<u8>> {
pub fn encrypt(&self, payload: Vec<u8>, nonce: Nonce) -> Option<Vec<u8>> {
let encryption_key =
aead::LessSafeKey::new(aead::UnboundKey::new(&aead::AES_256_GCM, &self.key).ok()?);

Expand All @@ -53,7 +43,7 @@ impl SharedKey {
Some(cipher_text)
}

pub fn decrypt(&self, cipher_text: Vec<u8>, nonce: [u8; NONCE_LEN]) -> Option<Vec<u8>> {
pub fn decrypt(&self, cipher_text: Vec<u8>, nonce: Nonce) -> Option<Vec<u8>> {
let decryption_key =
aead::LessSafeKey::new(aead::UnboundKey::new(&aead::AES_256_GCM, &self.key).ok()?);

Expand All @@ -76,6 +66,7 @@ impl SharedKey {
#[cfg(test)]
mod tests {
use eyre::OptionExt;
use rand::thread_rng;

use super::*;

Expand All @@ -86,10 +77,11 @@ mod tests {
let signer = PrivateKey::random(&mut csprng);
let verifier = PrivateKey::random(&mut csprng);

let (signer_shared_key, nonce) = SharedKey::new(&signer, &verifier.public_key());
let (verifier_shared_key, _nonce) = SharedKey::new(&verifier, &signer.public_key());
let signer_shared_key = SharedKey::new(&signer, &verifier.public_key());
let verifier_shared_key = SharedKey::new(&verifier, &signer.public_key());

let payload = b"privacy is important";
let nonce = [0u8; NONCE_LEN];

let encrypted_payload = signer_shared_key
.encrypt(payload.to_vec(), nonce)
Expand All @@ -113,10 +105,11 @@ mod tests {
let verifier = PrivateKey::random(&mut csprng);
let invalid = PrivateKey::random(&mut csprng);

let (signer_shared_key, nonce) = SharedKey::new(&signer, &verifier.public_key());
let (invalid_shared_key, _nonce) = SharedKey::new(&invalid, &invalid.public_key());
let signer_shared_key = SharedKey::new(&signer, &verifier.public_key());
let invalid_shared_key = SharedKey::new(&invalid, &invalid.public_key());

let token = b"privacy is important";
let nonce = [0u8; NONCE_LEN];

let encrypted_token = signer_shared_key
.encrypt(token.to_vec(), nonce)
Expand Down
7 changes: 4 additions & 3 deletions crates/node/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use calimero_context::config::ContextConfig;
use calimero_context::ContextManager;
use calimero_context_config::repr::ReprTransmute;
use calimero_context_config::ProposalAction;
use calimero_crypto::{SharedKey, NONCE_LEN};
use calimero_crypto::{Nonce, SharedKey, NONCE_LEN};
use calimero_network::client::NetworkClient;
use calimero_network::config::NetworkConfig;
use calimero_network::types::{NetworkEvent, PeerId};
Expand Down Expand Up @@ -344,7 +344,7 @@ impl Node {
return self.initiate_sync(context_id, source).await;
};

let (shared_key, _) = SharedKey::from_sk(&sender_key);
let shared_key = SharedKey::from_sk(&sender_key);

let artifact = &shared_key
.decrypt(artifact, nonce)
Expand Down Expand Up @@ -388,7 +388,8 @@ impl Node {
.get_sender_key(&context.id, &executor_public_key)?
.ok_or_eyre("expected own identity to have sender key")?;

let (shared_key, nonce) = SharedKey::from_sk(&sender_key);
let shared_key = SharedKey::from_sk(&sender_key);
let nonce = thread_rng().gen::<Nonce>();

let artifact_encrypted = shared_key
.encrypt(outcome.artifact.clone(), nonce)
Expand Down
37 changes: 16 additions & 21 deletions crates/node/src/sync.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::time::Duration;

use calimero_crypto::{SharedKey, NONCE_LEN};
use calimero_crypto::{Nonce, SharedKey};
use calimero_network::stream::{Message, Stream};
use calimero_primitives::context::ContextId;
use eyre::{bail, eyre, OptionExt, Result as EyreResult};
Expand Down Expand Up @@ -28,20 +28,15 @@ pub struct SyncConfig {
async fn send(
stream: &mut Stream,
message: &StreamMessage<'_>,
shared_key: Option<SharedKey>,
nonce: Option<[u8; NONCE_LEN]>,
shared_key: Option<(SharedKey, Nonce)>,
) -> EyreResult<()> {
let base_data = borsh::to_vec(message)?;

let data = match (shared_key, nonce) {
(Some(key), Some(nonce)) => key
let data = match shared_key {
Some((key, nonce)) => key
.encrypt(base_data, nonce)
.ok_or_eyre("encryption failed")?
.into_iter()
.chain(nonce.into_iter())
.collect(),
(Some(_), None) => bail!("nonce must be provided when encrypting"),
(None, _) => base_data,
.ok_or_eyre("encryption failed")?,
None => base_data,
};

stream.send(Message::new(data)).await?;
Expand All @@ -51,7 +46,7 @@ async fn send(
async fn recv(
stream: &mut Stream,
duration: Duration,
shared_key: Option<SharedKey>,
shared_key: Option<(SharedKey, Nonce)>,
) -> EyreResult<Option<StreamMessage<'static>>> {
let Some(message) = timeout(duration, stream.next()).await? else {
return Ok(None);
Expand All @@ -60,10 +55,9 @@ async fn recv(
let message_data = message?.data.into_owned();

let data = match shared_key {
Some(key) => {
let (message, nonce) = message_data.split_at(message_data.len() - NONCE_LEN);
Some((key, nonce)) => {
match key.decrypt(
message.to_vec(),
message_data,
nonce
.try_into()
.map_err(|_| eyre!("nonce must be 12 bytes"))?,
Expand Down Expand Up @@ -157,9 +151,7 @@ impl Node {
Err(err) => {
error!(%err, "Failed to handle stream message");

if let Err(err) =
send(&mut stream, &StreamMessage::OpaqueError, None, None).await
{
if let Err(err) = send(&mut stream, &StreamMessage::OpaqueError, None).await {
error!(%err, "Failed to send error message");
}
}
Expand All @@ -172,12 +164,14 @@ impl Node {
return Ok(None);
};

let (context_id, their_identity, payload) = match message {
let (context_id, their_identity, payload, nonce) = match message {
StreamMessage::Init {
context_id,
party_id,
payload,
} => (context_id, party_id, payload),
nonce,
..
} => (context_id, party_id, payload, nonce),
unexpected @ (StreamMessage::Message { .. } | StreamMessage::OpaqueError) => {
bail!("expected initialization handshake, got {:?}", unexpected)
}
Expand Down Expand Up @@ -215,7 +209,7 @@ impl Node {

match payload {
InitPayload::KeyShare => {
self.handle_key_share_request(&context, our_identity, their_identity, stream)
self.handle_key_share_request(&context, our_identity, their_identity, stream, nonce)
.await?
}
InitPayload::BlobShare { blob_id } => {
Expand Down Expand Up @@ -247,6 +241,7 @@ impl Node {
their_root_hash,
their_application_id,
stream,
nonce,
)
.await?
}
Expand Down
43 changes: 29 additions & 14 deletions crates/node/src/sync/blobs.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use calimero_crypto::SharedKey;
use calimero_crypto::{Nonce, SharedKey};
use calimero_network::stream::Stream;
use calimero_primitives::blobs::BlobId;
use calimero_primitives::context::Context;
use calimero_primitives::identity::PublicKey;
use eyre::{bail, OptionExt};
use futures_util::stream::poll_fn;
use futures_util::TryStreamExt;
use rand::{thread_rng, Rng};
use tokio::sync::mpsc;
use tracing::{debug, warn};

Expand All @@ -29,29 +30,32 @@ impl Node {
"Initiating blob share",
);

let nonce = thread_rng().gen::<Nonce>();

send(
stream,
&StreamMessage::Init {
context_id: context.id,
party_id: our_identity,
payload: InitPayload::BlobShare { blob_id },
nonce,
},
None,
None,
)
.await?;

let Some(ack) = recv(stream, self.sync_config.timeout, None).await? else {
bail!("connection closed while awaiting blob share handshake");
};

let their_identity = match ack {
let (their_identity, mut nonce) = match ack {
StreamMessage::Init {
party_id,
payload:
InitPayload::BlobShare {
blob_id: ack_blob_id,
},
nonce,
..
} => {
if ack_blob_id != blob_id {
Expand All @@ -62,7 +66,7 @@ impl Node {
);
}

party_id
(party_id, nonce)
}
unexpected @ (StreamMessage::Init { .. }
| StreamMessage::Message { .. }
Expand All @@ -76,7 +80,7 @@ impl Node {
.get_private_key(context.id, our_identity)?
.ok_or_eyre("expected own identity to have private key")?;

let (shared_key, _) = SharedKey::new(&private_key, &their_identity);
let shared_key = SharedKey::new(&private_key, &their_identity);

let (tx, mut rx) = mpsc::channel(1);

Expand All @@ -89,13 +93,16 @@ impl Node {
let read_task = async {
let mut sequencer = Sequencer::default();

while let Some(msg) = recv(stream, self.sync_config.timeout, Some(shared_key)).await? {
let (sequence_id, chunk) = match msg {
while let Some(msg) =
recv(stream, self.sync_config.timeout, Some((shared_key, nonce))).await?
{
let (sequence_id, chunk, new_nonce) = match msg {
StreamMessage::OpaqueError => bail!("other peer ran into an error"),
StreamMessage::Message {
sequence_id,
payload: MessagePayload::BlobShare { chunk },
} => (sequence_id, chunk),
nonce,
} => (sequence_id, chunk, nonce),
unexpected @ (StreamMessage::Init { .. } | StreamMessage::Message { .. }) => {
bail!("unexpected message: {:?}", unexpected)
}
Expand All @@ -108,6 +115,8 @@ impl Node {
}

tx.send(Ok(chunk)).await?;

nonce = new_nonce;
}

drop(tx);
Expand Down Expand Up @@ -163,45 +172,51 @@ impl Node {
.get_private_key(context.id, our_identity)?
.ok_or_eyre("expected own identity to have private key")?;

let (shared_key, nonce) = SharedKey::new(&private_key, &their_identity);
let shared_key = SharedKey::new(&private_key, &their_identity);
let mut nonce = thread_rng().gen::<Nonce>();

send(
stream,
&StreamMessage::Init {
context_id: context.id,
party_id: our_identity,
payload: InitPayload::BlobShare { blob_id },
nonce,
},
None,
None,
)
.await?;

let mut sequencer = Sequencer::default();

while let Some(chunk) = blob.try_next().await? {
let new_nonce = thread_rng().gen::<Nonce>();
send(
stream,
&StreamMessage::Message {
sequence_id: sequencer.next(),
payload: MessagePayload::BlobShare {
chunk: chunk.into_vec().into(),
},
nonce: new_nonce,
},
Some(shared_key),
Some(nonce),
Some((shared_key, nonce)),
)
.await?;

nonce = new_nonce;
}

let new_nonce = thread_rng().gen::<Nonce>();

send(
stream,
&StreamMessage::Message {
sequence_id: sequencer.next(),
payload: MessagePayload::BlobShare { chunk: b"".into() },
nonce: new_nonce,
},
Some(shared_key),
Some(nonce),
Some((shared_key, nonce)),
)
.await?;

Expand Down
Loading

0 comments on commit e201ffb

Please sign in to comment.