Skip to content

Commit 6518524

Browse files
Merge pull request #1288 from mintlayer/feat/e2e-encryption
End-to-end encryption for staking key passing to block production
2 parents 633e4ee + b6de6b8 commit 6518524

File tree

17 files changed

+582
-13
lines changed

17 files changed

+582
-13
lines changed

Cargo.lock

Lines changed: 18 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

blockprod/src/detail/mod.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,10 @@ use consensus::{
3939
generate_consensus_data_and_reward, ConsensusCreationError, ConsensusPoSError,
4040
FinalizeBlockInputData, GenerateBlockInputData, PoSFinalizeBlockInputData,
4141
};
42-
use crypto::random::{make_true_rng, Rng};
42+
use crypto::{
43+
ephemeral_e2e::{self, EndToEndPrivateKey},
44+
random::{make_true_rng, Rng},
45+
};
4346
use logging::log;
4447
use mempool::{
4548
tx_accumulator::{DefaultTxAccumulator, PackingStrategy, TransactionAccumulator},
@@ -116,6 +119,7 @@ pub struct BlockProduction {
116119
job_manager_handle: JobManagerHandle,
117120
mining_thread_pool: Arc<slave_pool::ThreadPool>,
118121
p2p_handle: P2pHandle,
122+
e2e_encryption_key: ephemeral_e2e::EndToEndPrivateKey,
119123
}
120124

121125
impl BlockProduction {
@@ -130,6 +134,8 @@ impl BlockProduction {
130134
) -> Result<Self, BlockProductionError> {
131135
let job_manager_handle = Box::new(JobManagerImpl::new(Some(chainstate_handle.clone())));
132136

137+
let mut rng = make_true_rng();
138+
133139
let block_production = Self {
134140
chain_config,
135141
blockprod_config,
@@ -139,6 +145,7 @@ impl BlockProduction {
139145
time_getter,
140146
job_manager_handle,
141147
mining_thread_pool,
148+
e2e_encryption_key: EndToEndPrivateKey::new_from_rng(&mut rng),
142149
};
143150

144151
Ok(block_production)
@@ -588,6 +595,10 @@ impl BlockProduction {
588595

589596
Ok(())
590597
}
598+
599+
pub fn e2e_private_key(&self) -> &ephemeral_e2e::EndToEndPrivateKey {
600+
&self.e2e_encryption_key
601+
}
591602
}
592603

593604
fn generate_finalize_block_data(

blockprod/src/interface/blockprod_interface.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use common::{
1919
primitives::Id,
2020
};
2121
use consensus::GenerateBlockInputData;
22+
use crypto::ephemeral_e2e;
2223
use mempool::tx_accumulator::PackingStrategy;
2324

2425
#[async_trait::async_trait]
@@ -47,4 +48,16 @@ pub trait BlockProductionInterface: Send + Sync {
4748
transaction_ids: Vec<Id<Transaction>>,
4849
packing_strategy: PackingStrategy,
4950
) -> Result<Block, BlockProductionError>;
51+
52+
async fn e2e_public_key(&self) -> ephemeral_e2e::EndToEndPublicKey;
53+
54+
/// Same as generate_block, but with end-to-end encryption for the secret data
55+
async fn generate_block_e2e(
56+
&mut self,
57+
encrypted_input_data: Vec<u8>,
58+
public_key: ephemeral_e2e::EndToEndPublicKey,
59+
transactions: Vec<SignedTransaction>,
60+
transaction_ids: Vec<Id<Transaction>>,
61+
packing_strategy: PackingStrategy,
62+
) -> Result<Block, BlockProductionError>;
5063
}

blockprod/src/interface/blockprod_interface_impl.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use common::{
2222
primitives::Id,
2323
};
2424
use consensus::GenerateBlockInputData;
25+
use crypto::ephemeral_e2e;
2526
use mempool::tx_accumulator::PackingStrategy;
2627

2728
use super::blockprod_interface::BlockProductionInterface;
@@ -52,6 +53,25 @@ impl BlockProductionInterface for BlockProduction {
5253

5354
Ok(block)
5455
}
56+
57+
async fn e2e_public_key(&self) -> ephemeral_e2e::EndToEndPublicKey {
58+
self.e2e_private_key().public_key()
59+
}
60+
61+
async fn generate_block_e2e(
62+
&mut self,
63+
encrypted_input_data: Vec<u8>,
64+
public_key: ephemeral_e2e::EndToEndPublicKey,
65+
transactions: Vec<SignedTransaction>,
66+
transaction_ids: Vec<Id<Transaction>>,
67+
packing_strategy: PackingStrategy,
68+
) -> Result<Block, BlockProductionError> {
69+
let shared_secret = self.e2e_private_key().shared_secret(&public_key);
70+
let input_data =
71+
shared_secret.decrypt_then_decode::<GenerateBlockInputData>(&encrypted_input_data)?;
72+
self.generate_block(input_data, transactions, transaction_ids, packing_strategy)
73+
.await
74+
}
5575
}
5676

5777
impl subsystem::Subsystem for Box<dyn BlockProductionInterface> {

blockprod/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ use common::{
2828
};
2929
use config::BlockProdConfig;
3030
use consensus::ConsensusCreationError;
31+
use crypto::ephemeral_e2e;
3132
use detail::{
3233
job_manager::{JobKey, JobManagerError},
3334
BlockProduction,
@@ -67,6 +68,8 @@ pub enum BlockProductionError {
6768
JobManagerError(#[from] JobManagerError),
6869
#[error("Mempool failed to construct block: {0}")]
6970
MempoolBlockConstruction(#[from] mempool::error::BlockConstructionError),
71+
#[error("Failed to decrypt generate-block input data: {0}")]
72+
E2eError(#[from] ephemeral_e2e::error::Error),
7073
}
7174

7275
pub type BlockProductionSubsystem = Box<dyn BlockProductionInterface>;

blockprod/src/rpc.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use common::{
2121
primitives::Id,
2222
};
2323
use consensus::GenerateBlockInputData;
24+
use crypto::ephemeral_e2e::{self, EndToEndPublicKey};
2425
use mempool::tx_accumulator::PackingStrategy;
2526
use rpc::Result as RpcResult;
2627
use serialization::hex_encoded::HexEncoded;
@@ -51,6 +52,19 @@ trait BlockProductionRpc {
5152
transaction_ids: Vec<Id<Transaction>>,
5253
packing_strategy: PackingStrategy,
5354
) -> RpcResult<HexEncoded<Block>>;
55+
56+
#[method(name = "e2e_public_key")]
57+
async fn e2e_public_key(&self) -> RpcResult<HexEncoded<ephemeral_e2e::EndToEndPublicKey>>;
58+
59+
#[method(name = "generate_block_e2e")]
60+
async fn generate_block_e2e(
61+
&self,
62+
encrypted_input_data: Vec<u8>,
63+
public_key: HexEncoded<EndToEndPublicKey>,
64+
transactions: Vec<HexEncoded<SignedTransaction>>,
65+
transaction_ids: Vec<Id<Transaction>>,
66+
packing_strategy: PackingStrategy,
67+
) -> RpcResult<HexEncoded<Block>>;
5468
}
5569

5670
#[async_trait::async_trait]
@@ -102,4 +116,38 @@ impl BlockProductionRpcServer for super::BlockProductionHandle {
102116

103117
Ok(block.into())
104118
}
119+
120+
async fn e2e_public_key(&self) -> rpc::Result<HexEncoded<EndToEndPublicKey>> {
121+
let public_key: EndToEndPublicKey =
122+
rpc::handle_result(self.call_async(move |this| this.e2e_public_key()).await)?;
123+
124+
Ok(public_key.into())
125+
}
126+
127+
async fn generate_block_e2e(
128+
&self,
129+
encrypted_input_data: Vec<u8>,
130+
public_key: HexEncoded<EndToEndPublicKey>,
131+
transactions: Vec<HexEncoded<SignedTransaction>>,
132+
transaction_ids: Vec<Id<Transaction>>,
133+
packing_strategy: PackingStrategy,
134+
) -> RpcResult<HexEncoded<Block>> {
135+
let transactions = transactions.into_iter().map(HexEncoded::take).collect::<Vec<_>>();
136+
let public_key = public_key.take();
137+
138+
let block: Block = rpc::handle_result(
139+
self.call_async_mut(move |this| {
140+
this.generate_block_e2e(
141+
encrypted_input_data,
142+
public_key,
143+
transactions,
144+
transaction_ids,
145+
packing_strategy,
146+
)
147+
})
148+
.await,
149+
)?;
150+
151+
Ok(block.into())
152+
}
105153
}

crypto/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ serialization = { path = "../serialization" }
1414
# The following crates don't work well with "workspace.dependencies"
1515
argon2 = { version = "0.5", features = ["std"] }
1616
merlin = { version = "3.0" , default-features = false }
17-
secp256k1 = { version = "0.27", default-features = false, features = ["global-context", "rand-std"] }
17+
secp256k1 = { version = "0.28", default-features = false, features = ["global-context", "rand-std"] }
1818

1919
bip39 = { workspace = true, default-features = false, features = ["std", "zeroize"] }
2020
blake2.workspace = true
@@ -35,6 +35,8 @@ sha3.workspace = true
3535
thiserror.workspace = true
3636
zeroize.workspace = true
3737

38+
x25519-dalek = { version = "2.0", features = ["reusable_secrets", "zeroize"] }
39+
3840
[dev-dependencies]
3941
test-utils = { path = "../test-utils" }
4042

crypto/src/ephemeral_e2e/error.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright (c) 2023 RBB S.r.l
2+
3+
// SPDX-License-Identifier: MIT
4+
// Licensed under the MIT License;
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// https://github.com/mintlayer/mintlayer-core/blob/master/LICENSE
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
#[derive(thiserror::Error, Debug, PartialEq, Eq, Clone)]
17+
pub enum Error {
18+
#[error("Symmetric key creation from shared secret failed: {0}")]
19+
SymmetricKeyCreationFailed(String),
20+
#[error("Symmetric encryption failed: {0}")]
21+
SymmetricEncryptionFailed(String),
22+
#[error("Symmetric decryption failed: {0}")]
23+
SymmetricDecryptionFailed(String),
24+
#[error("Deserialization failed: {0}")]
25+
DeserializationFailed(String),
26+
}

0 commit comments

Comments
 (0)