mirror of
https://github.com/mimblewimble/mwixnet.git
synced 2025-01-20 19:11:09 +03:00
switch to x25519 for onion encryption
This commit is contained in:
parent
f70e1dfbf2
commit
c59688a045
6 changed files with 136 additions and 111 deletions
26
Cargo.lock
generated
26
Cargo.lock
generated
|
@ -45,7 +45,7 @@ dependencies = [
|
||||||
"nom 7.1.0",
|
"nom 7.1.0",
|
||||||
"pin-project",
|
"pin-project",
|
||||||
"rand 0.7.3",
|
"rand 0.7.3",
|
||||||
"rand 0.8.4",
|
"rand 0.8.5",
|
||||||
"rust-embed",
|
"rust-embed",
|
||||||
"scrypt",
|
"scrypt",
|
||||||
"sha2 0.9.8",
|
"sha2 0.9.8",
|
||||||
|
@ -65,7 +65,7 @@ dependencies = [
|
||||||
"cookie-factory",
|
"cookie-factory",
|
||||||
"hkdf",
|
"hkdf",
|
||||||
"nom 7.1.0",
|
"nom 7.1.0",
|
||||||
"rand 0.8.4",
|
"rand 0.8.5",
|
||||||
"secrecy 0.8.0",
|
"secrecy 0.8.0",
|
||||||
"sha2 0.9.8",
|
"sha2 0.9.8",
|
||||||
]
|
]
|
||||||
|
@ -2475,7 +2475,9 @@ dependencies = [
|
||||||
"bytes 0.5.6",
|
"bytes 0.5.6",
|
||||||
"chacha20",
|
"chacha20",
|
||||||
"clap",
|
"clap",
|
||||||
|
"curve25519-dalek 2.1.3",
|
||||||
"dirs",
|
"dirs",
|
||||||
|
"ed25519-dalek",
|
||||||
"futures 0.3.17",
|
"futures 0.3.17",
|
||||||
"grin_api 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)",
|
"grin_api 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)",
|
||||||
"grin_chain 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)",
|
"grin_chain 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)",
|
||||||
|
@ -2488,6 +2490,7 @@ dependencies = [
|
||||||
"grin_wallet_api",
|
"grin_wallet_api",
|
||||||
"grin_wallet_impls",
|
"grin_wallet_impls",
|
||||||
"grin_wallet_libwallet",
|
"grin_wallet_libwallet",
|
||||||
|
"grin_wallet_util",
|
||||||
"hmac 0.12.0",
|
"hmac 0.12.0",
|
||||||
"hyper 0.14.14",
|
"hyper 0.14.14",
|
||||||
"itertools",
|
"itertools",
|
||||||
|
@ -2496,7 +2499,7 @@ dependencies = [
|
||||||
"jsonrpc-http-server",
|
"jsonrpc-http-server",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"pbkdf2 0.8.0",
|
"pbkdf2 0.8.0",
|
||||||
"rand 0.8.4",
|
"rand 0.7.3",
|
||||||
"ring",
|
"ring",
|
||||||
"rpassword",
|
"rpassword",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -2506,6 +2509,7 @@ dependencies = [
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio 1.12.0",
|
"tokio 1.12.0",
|
||||||
"toml",
|
"toml",
|
||||||
|
"x25519-dalek 0.6.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -3033,14 +3037,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.8.4"
|
version = "0.8.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"rand_chacha 0.3.1",
|
"rand_chacha 0.3.1",
|
||||||
"rand_core 0.6.3",
|
"rand_core 0.6.3",
|
||||||
"rand_hc 0.3.1",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -3124,15 +3127,6 @@ dependencies = [
|
||||||
"rand_core 0.5.1",
|
"rand_core 0.5.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_hc"
|
|
||||||
version = "0.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
|
|
||||||
dependencies = [
|
|
||||||
"rand_core 0.6.3",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand_isaac"
|
name = "rand_isaac"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
|
@ -3854,7 +3848,7 @@ checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"libc",
|
"libc",
|
||||||
"rand 0.8.4",
|
"rand 0.8.5",
|
||||||
"redox_syscall 0.2.10",
|
"redox_syscall 0.2.10",
|
||||||
"remove_dir_all",
|
"remove_dir_all",
|
||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
|
|
|
@ -11,7 +11,9 @@ byteorder = "1"
|
||||||
bytes = "0.5.6"
|
bytes = "0.5.6"
|
||||||
chacha20 = "0.8.1"
|
chacha20 = "0.8.1"
|
||||||
clap = { version = "2.33", features = ["yaml"] }
|
clap = { version = "2.33", features = ["yaml"] }
|
||||||
|
curve25519-dalek = "2.1"
|
||||||
dirs = "2.0"
|
dirs = "2.0"
|
||||||
|
ed25519-dalek = "1.0.1"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
hmac = { version = "0.12.0", features = ["std"]}
|
hmac = { version = "0.12.0", features = ["std"]}
|
||||||
hyper = { version = "0.14", features = ["full"] }
|
hyper = { version = "0.14", features = ["full"] }
|
||||||
|
@ -21,7 +23,7 @@ jsonrpc-derive = "18.0"
|
||||||
jsonrpc-http-server = "18.0"
|
jsonrpc-http-server = "18.0"
|
||||||
lazy_static = "1"
|
lazy_static = "1"
|
||||||
pbkdf2 = "0.8.0"
|
pbkdf2 = "0.8.0"
|
||||||
rand = "0.8.4"
|
rand = "0.7.3"
|
||||||
ring = "0.16"
|
ring = "0.16"
|
||||||
rpassword = "4.0"
|
rpassword = "4.0"
|
||||||
serde = { version = "1", features= ["derive"]}
|
serde = { version = "1", features= ["derive"]}
|
||||||
|
@ -31,6 +33,7 @@ sha2 = "0.10.0"
|
||||||
thiserror = "1.0.31"
|
thiserror = "1.0.31"
|
||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
toml = "0.5"
|
toml = "0.5"
|
||||||
|
x25519-dalek = "0.6.0"
|
||||||
grin_secp256k1zkp = { version = "0.7.11", features = ["bullet-proof-sizing"]}
|
grin_secp256k1zkp = { version = "0.7.11", features = ["bullet-proof-sizing"]}
|
||||||
grin_util = "5"
|
grin_util = "5"
|
||||||
grin_api = { git = "https://github.com/mimblewimble/grin", version = "5.2.0-alpha.1" }
|
grin_api = { git = "https://github.com/mimblewimble/grin", version = "5.2.0-alpha.1" }
|
||||||
|
@ -41,4 +44,5 @@ grin_servers = { git = "https://github.com/mimblewimble/grin", version = "5.2.0-
|
||||||
grin_store = { git = "https://github.com/mimblewimble/grin", version = "5.2.0-alpha.1" }
|
grin_store = { git = "https://github.com/mimblewimble/grin", version = "5.2.0-alpha.1" }
|
||||||
grin_wallet_api = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" }
|
grin_wallet_api = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" }
|
||||||
grin_wallet_impls = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" }
|
grin_wallet_impls = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" }
|
||||||
grin_wallet_libwallet = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" }
|
grin_wallet_libwallet = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" }
|
||||||
|
grin_wallet_util = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" }
|
40
src/main.rs
40
src/main.rs
|
@ -190,28 +190,30 @@ fn real_main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
async fn build_signals_fut() {
|
async fn build_signals_fut() {
|
||||||
if cfg!(unix) {
|
use tokio::signal::unix::{signal, SignalKind};
|
||||||
use tokio::signal::unix::{signal, SignalKind};
|
|
||||||
|
|
||||||
// Listen for SIGINT, SIGQUIT, and SIGTERM
|
// Listen for SIGINT, SIGQUIT, and SIGTERM
|
||||||
let mut terminate_signal =
|
let mut terminate_signal =
|
||||||
signal(SignalKind::terminate()).expect("failed to create terminate signal");
|
signal(SignalKind::terminate()).expect("failed to create terminate signal");
|
||||||
let mut quit_signal = signal(SignalKind::quit()).expect("failed to create quit signal");
|
let mut quit_signal = signal(SignalKind::quit()).expect("failed to create quit signal");
|
||||||
let mut interrupt_signal =
|
let mut interrupt_signal =
|
||||||
signal(SignalKind::interrupt()).expect("failed to create interrupt signal");
|
signal(SignalKind::interrupt()).expect("failed to create interrupt signal");
|
||||||
|
|
||||||
futures::future::select_all(vec![
|
futures::future::select_all(vec![
|
||||||
Box::pin(terminate_signal.recv()),
|
Box::pin(terminate_signal.recv()),
|
||||||
Box::pin(quit_signal.recv()),
|
Box::pin(quit_signal.recv()),
|
||||||
Box::pin(interrupt_signal.recv()),
|
Box::pin(interrupt_signal.recv()),
|
||||||
])
|
])
|
||||||
.await;
|
.await;
|
||||||
} else {
|
}
|
||||||
tokio::signal::ctrl_c()
|
|
||||||
.await
|
#[cfg(not(unix))]
|
||||||
.expect("failed to install CTRL+C signal handler");
|
async fn build_signals_fut() {
|
||||||
}
|
tokio::signal::ctrl_c()
|
||||||
|
.await
|
||||||
|
.expect("failed to install CTRL+C signal handler");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prompt_password() -> ZeroingString {
|
fn prompt_password() -> ZeroingString {
|
||||||
|
|
157
src/onion.rs
157
src/onion.rs
|
@ -1,34 +1,62 @@
|
||||||
use crate::secp::{self, Commitment, PublicKey, Secp256k1, SecretKey, SharedSecret};
|
use crate::secp::{self, Commitment, SecretKey};
|
||||||
use crate::types::Payload;
|
use crate::types::Payload;
|
||||||
|
|
||||||
use crate::onion::OnionError::{InvalidKeyLength, SerializationError};
|
use crate::onion::OnionError::{InvalidKeyLength, SerializationError};
|
||||||
use chacha20::cipher::{NewCipher, StreamCipher};
|
use chacha20::cipher::{NewCipher, StreamCipher};
|
||||||
use chacha20::{ChaCha20, Key, Nonce};
|
use chacha20::{ChaCha20, Key, Nonce};
|
||||||
use grin_core::ser::{self, ProtocolVersion, Readable, Reader, Writeable, Writer};
|
use grin_core::ser::{self, Readable, Reader, Writeable, Writer};
|
||||||
use grin_util::{self, ToHex};
|
use grin_util::{self, ToHex};
|
||||||
use hmac::digest::InvalidLength;
|
use hmac::digest::InvalidLength;
|
||||||
use hmac::{Hmac, Mac};
|
use hmac::{Hmac, Mac};
|
||||||
use serde::ser::SerializeStruct;
|
use serde::ser::SerializeStruct;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
|
use std::convert::TryInto;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
use std::result::Result;
|
use std::result::Result;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
use x25519_dalek::{PublicKey as xPublicKey, SharedSecret, StaticSecret};
|
||||||
|
|
||||||
type HmacSha256 = Hmac<Sha256>;
|
type HmacSha256 = Hmac<Sha256>;
|
||||||
type RawBytes = Vec<u8>;
|
type RawBytes = Vec<u8>;
|
||||||
|
|
||||||
/// A data packet with layers of encryption
|
/// A data packet with layers of encryption
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Onion {
|
pub struct Onion {
|
||||||
/// The onion originator's portion of the shared secret
|
/// The onion originator's portion of the shared secret
|
||||||
pub ephemeral_pubkey: PublicKey,
|
pub ephemeral_pubkey: xPublicKey,
|
||||||
/// The pedersen commitment before adjusting the excess and subtracting the fee
|
/// The pedersen commitment before adjusting the excess and subtracting the fee
|
||||||
pub commit: Commitment,
|
pub commit: Commitment,
|
||||||
/// The encrypted payloads which represent the layers of the onion
|
/// The encrypted payloads which represent the layers of the onion
|
||||||
pub enc_payloads: Vec<RawBytes>,
|
pub enc_payloads: Vec<RawBytes>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Onion {
|
||||||
|
fn eq(&self, other: &Onion) -> bool {
|
||||||
|
*self.ephemeral_pubkey.as_bytes() == *other.ephemeral_pubkey.as_bytes()
|
||||||
|
&& self.commit == other.commit
|
||||||
|
&& self.enc_payloads == other.enc_payloads
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for Onion {}
|
||||||
|
|
||||||
|
impl Hash for Onion {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
state.write(self.ephemeral_pubkey.as_bytes());
|
||||||
|
state.write(self.commit.as_ref());
|
||||||
|
state.write_usize(self.enc_payloads.len());
|
||||||
|
for p in &self.enc_payloads {
|
||||||
|
state.write(p.as_slice());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vec_to_32_byte_arr(v: Vec<u8>) -> Result<[u8; 32], OnionError> {
|
||||||
|
v.try_into().map_err(|_| InvalidKeyLength)
|
||||||
|
}
|
||||||
|
|
||||||
impl Onion {
|
impl Onion {
|
||||||
pub fn serialize(&self) -> Result<Vec<u8>, ser::Error> {
|
pub fn serialize(&self) -> Result<Vec<u8>, ser::Error> {
|
||||||
let mut vec = vec![];
|
let mut vec = vec![];
|
||||||
|
@ -38,9 +66,7 @@ impl Onion {
|
||||||
|
|
||||||
/// Peel a single layer off of the Onion, returning the peeled Onion and decrypted Payload
|
/// Peel a single layer off of the Onion, returning the peeled Onion and decrypted Payload
|
||||||
pub fn peel_layer(&self, secret_key: &SecretKey) -> Result<(Payload, Onion), OnionError> {
|
pub fn peel_layer(&self, secret_key: &SecretKey) -> Result<(Payload, Onion), OnionError> {
|
||||||
let secp = Secp256k1::new();
|
let shared_secret = StaticSecret::from(secret_key.0).diffie_hellman(&self.ephemeral_pubkey);
|
||||||
|
|
||||||
let shared_secret = SharedSecret::new(&secp, &self.ephemeral_pubkey, &secret_key);
|
|
||||||
let mut cipher = new_stream_cipher(&shared_secret)?;
|
let mut cipher = new_stream_cipher(&shared_secret)?;
|
||||||
|
|
||||||
let mut decrypted_bytes = self.enc_payloads[0].clone();
|
let mut decrypted_bytes = self.enc_payloads[0].clone();
|
||||||
|
@ -62,10 +88,12 @@ impl Onion {
|
||||||
|
|
||||||
let blinding_factor = calc_blinding_factor(&shared_secret, &self.ephemeral_pubkey)?;
|
let blinding_factor = calc_blinding_factor(&shared_secret, &self.ephemeral_pubkey)?;
|
||||||
|
|
||||||
let mut ephemeral_pubkey = self.ephemeral_pubkey.clone();
|
let ephemeral_key = StaticSecret::from(
|
||||||
ephemeral_pubkey
|
*blinding_factor
|
||||||
.mul_assign(&secp, &blinding_factor)
|
.diffie_hellman(&self.ephemeral_pubkey)
|
||||||
.map_err(|e| OnionError::CalcPubKeyError(e))?;
|
.as_bytes(),
|
||||||
|
);
|
||||||
|
let ephemeral_pubkey = xPublicKey::from(&ephemeral_key);
|
||||||
|
|
||||||
let mut commitment = self.commit.clone();
|
let mut commitment = self.commit.clone();
|
||||||
commitment = secp::add_excess(&commitment, &decrypted_payload.excess)
|
commitment = secp::add_excess(&commitment, &decrypted_payload.excess)
|
||||||
|
@ -84,23 +112,23 @@ impl Onion {
|
||||||
|
|
||||||
fn calc_blinding_factor(
|
fn calc_blinding_factor(
|
||||||
shared_secret: &SharedSecret,
|
shared_secret: &SharedSecret,
|
||||||
ephemeral_pubkey: &PublicKey,
|
ephemeral_pubkey: &xPublicKey,
|
||||||
) -> Result<SecretKey, OnionError> {
|
) -> Result<StaticSecret, OnionError> {
|
||||||
let serialized_pubkey = ser::ser_vec(&ephemeral_pubkey, ProtocolVersion::local())?;
|
|
||||||
|
|
||||||
let mut hasher = Sha256::default();
|
let mut hasher = Sha256::default();
|
||||||
hasher.update(&serialized_pubkey);
|
hasher.update(ephemeral_pubkey.as_bytes());
|
||||||
hasher.update(&shared_secret[0..32]);
|
hasher.update(shared_secret.as_bytes());
|
||||||
|
let hashed: [u8; 32] = hasher
|
||||||
|
.finalize()
|
||||||
|
.as_slice()
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| InvalidKeyLength)?;
|
||||||
|
|
||||||
let secp = Secp256k1::new();
|
Ok(StaticSecret::from(hashed))
|
||||||
let blind = SecretKey::from_slice(&secp, &hasher.finalize())
|
|
||||||
.map_err(|e| OnionError::CalcBlindError(e))?;
|
|
||||||
Ok(blind)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_stream_cipher(shared_secret: &SharedSecret) -> Result<ChaCha20, OnionError> {
|
fn new_stream_cipher(shared_secret: &SharedSecret) -> Result<ChaCha20, OnionError> {
|
||||||
let mut mu_hmac = HmacSha256::new_from_slice(b"MWIXNET")?;
|
let mut mu_hmac = HmacSha256::new_from_slice(b"MWIXNET")?;
|
||||||
mu_hmac.update(&shared_secret[0..32]);
|
mu_hmac.update(shared_secret.as_bytes());
|
||||||
let mukey = mu_hmac.finalize().into_bytes();
|
let mukey = mu_hmac.finalize().into_bytes();
|
||||||
|
|
||||||
let key = Key::from_slice(&mukey[0..32]);
|
let key = Key::from_slice(&mukey[0..32]);
|
||||||
|
@ -111,7 +139,7 @@ fn new_stream_cipher(shared_secret: &SharedSecret) -> Result<ChaCha20, OnionErro
|
||||||
|
|
||||||
impl Writeable for Onion {
|
impl Writeable for Onion {
|
||||||
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
|
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
|
||||||
self.ephemeral_pubkey.write(writer)?;
|
writer.write_fixed_bytes(self.ephemeral_pubkey.as_bytes())?;
|
||||||
writer.write_fixed_bytes(&self.commit)?;
|
writer.write_fixed_bytes(&self.commit)?;
|
||||||
writer.write_u64(self.enc_payloads.len() as u64)?;
|
writer.write_u64(self.enc_payloads.len() as u64)?;
|
||||||
for p in &self.enc_payloads {
|
for p in &self.enc_payloads {
|
||||||
|
@ -124,7 +152,9 @@ impl Writeable for Onion {
|
||||||
|
|
||||||
impl Readable for Onion {
|
impl Readable for Onion {
|
||||||
fn read<R: Reader>(reader: &mut R) -> Result<Onion, ser::Error> {
|
fn read<R: Reader>(reader: &mut R) -> Result<Onion, ser::Error> {
|
||||||
let ephemeral_pubkey = PublicKey::read(reader)?;
|
let pubkey_bytes: [u8; 32] =
|
||||||
|
vec_to_32_byte_arr(reader.read_fixed_bytes(32)?).map_err(|_| ser::Error::CountError)?;
|
||||||
|
let ephemeral_pubkey = xPublicKey::from(pubkey_bytes);
|
||||||
let commit = Commitment::read(reader)?;
|
let commit = Commitment::read(reader)?;
|
||||||
let mut enc_payloads: Vec<RawBytes> = Vec::new();
|
let mut enc_payloads: Vec<RawBytes> = Vec::new();
|
||||||
let len = reader.read_u64()?;
|
let len = reader.read_u64()?;
|
||||||
|
@ -148,11 +178,7 @@ impl serde::ser::Serialize for Onion {
|
||||||
{
|
{
|
||||||
let mut state = serializer.serialize_struct("Onion", 3)?;
|
let mut state = serializer.serialize_struct("Onion", 3)?;
|
||||||
|
|
||||||
let secp = Secp256k1::new();
|
state.serialize_field("pubkey", &self.ephemeral_pubkey.as_bytes().to_hex())?;
|
||||||
state.serialize_field(
|
|
||||||
"pubkey",
|
|
||||||
&self.ephemeral_pubkey.serialize_vec(&secp, true).to_hex(),
|
|
||||||
)?;
|
|
||||||
state.serialize_field("commit", &self.commit.to_hex())?;
|
state.serialize_field("commit", &self.commit.to_hex())?;
|
||||||
|
|
||||||
let hex_payloads: Vec<String> = self.enc_payloads.iter().map(|v| v.to_hex()).collect();
|
let hex_payloads: Vec<String> = self.enc_payloads.iter().map(|v| v.to_hex()).collect();
|
||||||
|
@ -197,11 +223,10 @@ impl<'de> serde::de::Deserialize<'de> for Onion {
|
||||||
let val: String = map.next_value()?;
|
let val: String = map.next_value()?;
|
||||||
let vec =
|
let vec =
|
||||||
grin_util::from_hex(&val).map_err(serde::de::Error::custom)?;
|
grin_util::from_hex(&val).map_err(serde::de::Error::custom)?;
|
||||||
let secp = Secp256k1::new();
|
pubkey =
|
||||||
pubkey = Some(
|
Some(xPublicKey::from(vec_to_32_byte_arr(vec).map_err(|_| {
|
||||||
PublicKey::from_slice(&secp, &vec[..])
|
serde::de::Error::custom("Invalid length pubkey")
|
||||||
.map_err(serde::de::Error::custom)?,
|
})?));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Field::Commit => {
|
Field::Commit => {
|
||||||
let val: String = map.next_value()?;
|
let val: String = map.next_value()?;
|
||||||
|
@ -267,40 +292,49 @@ impl From<ser::Error> for OnionError {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod test_util {
|
pub mod test_util {
|
||||||
use super::{Onion, OnionError, RawBytes};
|
use super::{Onion, OnionError, RawBytes};
|
||||||
use crate::secp::test_util::{rand_commit, rand_proof, rand_pubkey};
|
use crate::secp::test_util::{rand_commit, rand_proof};
|
||||||
use crate::secp::{self, Commitment, PublicKey, Secp256k1, SecretKey, SharedSecret};
|
use crate::secp::{random_secret, Commitment, SecretKey};
|
||||||
use crate::types::Payload;
|
use crate::types::Payload;
|
||||||
|
|
||||||
use chacha20::cipher::StreamCipher;
|
use chacha20::cipher::StreamCipher;
|
||||||
use grin_core::core::FeeFields;
|
use grin_core::core::FeeFields;
|
||||||
use rand::RngCore;
|
use rand::{thread_rng, RngCore};
|
||||||
|
use x25519_dalek::PublicKey as xPublicKey;
|
||||||
|
use x25519_dalek::{SharedSecret, StaticSecret};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Hop {
|
pub struct Hop {
|
||||||
pub pubkey: PublicKey,
|
pub pubkey: xPublicKey,
|
||||||
pub payload: Payload,
|
pub payload: Payload,
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
Choose random xi for each node ni and create a Payload (Pi) for each containing xi
|
||||||
|
Build a rangeproof for Cn=Cin+(Σx1...n)*G and include it in payload Pn
|
||||||
|
Choose random initial ephemeral keypair (r1, R1)
|
||||||
|
Derive remaining ephemeral keypairs such that ri+1=ri*Sha256(Ri||si) where si=ECDH(Ri, Ki)
|
||||||
|
For each node ni, use ChaCha20 stream cipher with key=HmacSha256("MWIXNET"||si) and nonce "NONCE1234567" to encrypt payloads Pi...n
|
||||||
|
*/
|
||||||
|
|
||||||
/// Create an Onion for the Commitment, encrypting the payload for each hop
|
/// Create an Onion for the Commitment, encrypting the payload for each hop
|
||||||
pub fn create_onion(commitment: &Commitment, hops: &Vec<Hop>) -> Result<Onion, OnionError> {
|
pub fn create_onion(commitment: &Commitment, hops: &Vec<Hop>) -> Result<Onion, OnionError> {
|
||||||
let secp = Secp256k1::new();
|
let initial_key = StaticSecret::new(&mut thread_rng());
|
||||||
let session_key = secp::random_secret();
|
let mut ephemeral_key = initial_key.clone();
|
||||||
let mut ephemeral_key = session_key.clone();
|
|
||||||
|
|
||||||
let mut shared_secrets: Vec<SharedSecret> = Vec::new();
|
let mut shared_secrets: Vec<SharedSecret> = Vec::new();
|
||||||
let mut enc_payloads: Vec<RawBytes> = Vec::new();
|
let mut enc_payloads: Vec<RawBytes> = Vec::new();
|
||||||
for hop in hops {
|
for hop in hops {
|
||||||
let shared_secret = SharedSecret::new(&secp, &hop.pubkey, &ephemeral_key);
|
let shared_secret = ephemeral_key.diffie_hellman(&hop.pubkey);
|
||||||
|
|
||||||
let ephemeral_pubkey = PublicKey::from_secret_key(&secp, &ephemeral_key)
|
let ephemeral_pubkey = xPublicKey::from(&ephemeral_key);
|
||||||
.map_err(|e| OnionError::CalcPubKeyError(e))?;
|
|
||||||
let blinding_factor = super::calc_blinding_factor(&shared_secret, &ephemeral_pubkey)?;
|
let blinding_factor = super::calc_blinding_factor(&shared_secret, &ephemeral_pubkey)?;
|
||||||
|
|
||||||
shared_secrets.push(shared_secret);
|
shared_secrets.push(shared_secret);
|
||||||
enc_payloads.push(hop.payload.serialize()?);
|
enc_payloads.push(hop.payload.serialize()?);
|
||||||
ephemeral_key
|
ephemeral_key = StaticSecret::from(
|
||||||
.mul_assign(&secp, &blinding_factor)
|
*ephemeral_key
|
||||||
.map_err(|e| OnionError::CalcPubKeyError(e))?;
|
.diffie_hellman(&xPublicKey::from(&blinding_factor))
|
||||||
|
.as_bytes(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in (0..shared_secrets.len()).rev() {
|
for i in (0..shared_secrets.len()).rev() {
|
||||||
|
@ -311,8 +345,7 @@ pub mod test_util {
|
||||||
}
|
}
|
||||||
|
|
||||||
let onion = Onion {
|
let onion = Onion {
|
||||||
ephemeral_pubkey: PublicKey::from_secret_key(&secp, &session_key)
|
ephemeral_pubkey: xPublicKey::from(&initial_key),
|
||||||
.map_err(|e| OnionError::CalcPubKeyError(e))?,
|
|
||||||
commit: commitment.clone(),
|
commit: commitment.clone(),
|
||||||
enc_payloads,
|
enc_payloads,
|
||||||
};
|
};
|
||||||
|
@ -322,13 +355,13 @@ pub mod test_util {
|
||||||
pub fn rand_onion() -> Onion {
|
pub fn rand_onion() -> Onion {
|
||||||
let commit = rand_commit();
|
let commit = rand_commit();
|
||||||
let mut hops = Vec::new();
|
let mut hops = Vec::new();
|
||||||
let k = (rand::thread_rng().next_u64() % 5) + 1;
|
let k = (thread_rng().next_u64() % 5) + 1;
|
||||||
for i in 0..k {
|
for i in 0..k {
|
||||||
let hop = Hop {
|
let hop = Hop {
|
||||||
pubkey: rand_pubkey(),
|
pubkey: xPublicKey::from(random_secret().0),
|
||||||
payload: Payload {
|
payload: Payload {
|
||||||
excess: secp::random_secret(),
|
excess: random_secret(),
|
||||||
fee: FeeFields::from(rand::thread_rng().next_u32()),
|
fee: FeeFields::from(thread_rng().next_u32()),
|
||||||
rangeproof: if i == (k - 1) {
|
rangeproof: if i == (k - 1) {
|
||||||
Some(rand_proof())
|
Some(rand_proof())
|
||||||
} else {
|
} else {
|
||||||
|
@ -346,15 +379,12 @@ pub mod test_util {
|
||||||
pub fn next_ephemeral_pubkey(
|
pub fn next_ephemeral_pubkey(
|
||||||
onion: &Onion,
|
onion: &Onion,
|
||||||
server_key: &SecretKey,
|
server_key: &SecretKey,
|
||||||
) -> Result<PublicKey, OnionError> {
|
) -> Result<xPublicKey, OnionError> {
|
||||||
let secp = Secp256k1::new();
|
let shared_secret =
|
||||||
let mut ephemeral_pubkey = onion.ephemeral_pubkey.clone();
|
StaticSecret::from(server_key.0.clone()).diffie_hellman(&onion.ephemeral_pubkey);
|
||||||
let shared_secret = SharedSecret::new(&secp, &ephemeral_pubkey, &server_key);
|
let blinding_factor = super::calc_blinding_factor(&shared_secret, &onion.ephemeral_pubkey)?;
|
||||||
let blinding_factor = super::calc_blinding_factor(&shared_secret, &ephemeral_pubkey)?;
|
let mul = blinding_factor.diffie_hellman(&onion.ephemeral_pubkey);
|
||||||
ephemeral_pubkey
|
Ok(xPublicKey::from(&StaticSecret::from(*mul.as_bytes())))
|
||||||
.mul_assign(&secp, &blinding_factor)
|
|
||||||
.map_err(|e| OnionError::CalcPubKeyError(e))?;
|
|
||||||
Ok(ephemeral_pubkey)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,6 +395,7 @@ pub mod tests {
|
||||||
use crate::types::Payload;
|
use crate::types::Payload;
|
||||||
|
|
||||||
use grin_core::core::FeeFields;
|
use grin_core::core::FeeFields;
|
||||||
|
use x25519_dalek::{PublicKey as xPublicKey, StaticSecret};
|
||||||
|
|
||||||
/// Test end-to-end Onion creation and unwrapping logic.
|
/// Test end-to-end Onion creation and unwrapping logic.
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -405,7 +436,7 @@ pub mod tests {
|
||||||
};
|
};
|
||||||
|
|
||||||
hops.push(Hop {
|
hops.push(Hop {
|
||||||
pubkey: secp::PublicKey::from_secret_key(&secp, &keys[i]).unwrap(),
|
pubkey: xPublicKey::from(&StaticSecret::from(keys[i].0.clone())),
|
||||||
payload: Payload {
|
payload: Payload {
|
||||||
excess,
|
excess,
|
||||||
fee: FeeFields::from(fee_per_hop as u32),
|
fee: FeeFields::from(fee_per_hop as u32),
|
||||||
|
|
|
@ -218,7 +218,7 @@ pub fn sign(sk: &SecretKey, msg: &Message) -> Result<Signature, secp256k1zkp::Er
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod test_util {
|
pub mod test_util {
|
||||||
use crate::secp::{self, Commitment, PublicKey, RangeProof, Secp256k1};
|
use crate::secp::{self, Commitment, RangeProof, Secp256k1};
|
||||||
use grin_core::core::hash::Hash;
|
use grin_core::core::hash::Hash;
|
||||||
use grin_util::ToHex;
|
use grin_util::ToHex;
|
||||||
use rand::RngCore;
|
use rand::RngCore;
|
||||||
|
@ -242,11 +242,6 @@ pub mod test_util {
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rand_pubkey() -> PublicKey {
|
|
||||||
let secp = Secp256k1::new();
|
|
||||||
PublicKey::from_secret_key(&secp, &secp::random_secret()).unwrap()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -270,9 +270,7 @@ mod tests {
|
||||||
use crate::node::mock::MockGrinNode;
|
use crate::node::mock::MockGrinNode;
|
||||||
use crate::onion::test_util::{self, Hop};
|
use crate::onion::test_util::{self, Hop};
|
||||||
use crate::onion::Onion;
|
use crate::onion::Onion;
|
||||||
use crate::secp::{
|
use crate::secp::{self, ComSignature, Commitment, RangeProof, Secp256k1, SecretKey};
|
||||||
self, ComSignature, Commitment, PublicKey, RangeProof, Secp256k1, SecretKey,
|
|
||||||
};
|
|
||||||
use crate::server::{Server, ServerImpl, SwapError};
|
use crate::server::{Server, ServerImpl, SwapError};
|
||||||
use crate::store::{SwapData, SwapStatus, SwapStore};
|
use crate::store::{SwapData, SwapStatus, SwapStore};
|
||||||
use crate::types::Payload;
|
use crate::types::Payload;
|
||||||
|
@ -283,6 +281,8 @@ mod tests {
|
||||||
use grin_core::global::{self, ChainTypes};
|
use grin_core::global::{self, ChainTypes};
|
||||||
use std::net::TcpListener;
|
use std::net::TcpListener;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use x25519_dalek::PublicKey as xPublicKey;
|
||||||
|
use x25519_dalek::StaticSecret;
|
||||||
|
|
||||||
macro_rules! assert_error_type {
|
macro_rules! assert_error_type {
|
||||||
($result:expr, $error_type:pat) => {
|
($result:expr, $error_type:pat) => {
|
||||||
|
@ -351,9 +351,8 @@ mod tests {
|
||||||
fee: u64,
|
fee: u64,
|
||||||
proof: Option<RangeProof>,
|
proof: Option<RangeProof>,
|
||||||
) -> Hop {
|
) -> Hop {
|
||||||
let secp = Secp256k1::new();
|
|
||||||
Hop {
|
Hop {
|
||||||
pubkey: PublicKey::from_secret_key(&secp, &server_key).unwrap(),
|
pubkey: xPublicKey::from(&StaticSecret::from(server_key.0.clone())),
|
||||||
payload: Payload {
|
payload: Payload {
|
||||||
excess: hop_excess.clone(),
|
excess: hop_excess.clone(),
|
||||||
fee: FeeFields::from(fee as u32),
|
fee: FeeFields::from(fee as u32),
|
||||||
|
|
Loading…
Reference in a new issue