implement message cipher via xmr view key

This commit is contained in:
creating2morrow 2024-04-06 09:31:50 -04:00
parent 4334f2bee7
commit e245be7143
11 changed files with 196 additions and 126 deletions

10
Cargo.lock generated
View file

@ -300,15 +300,6 @@ dependencies = [
"objc2-encode",
]
[[package]]
name = "bs58"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4"
dependencies = [
"tinyvec",
]
[[package]]
name = "build-rs"
version = "0.1.2"
@ -2032,7 +2023,6 @@ dependencies = [
name = "neveko_core"
version = "0.1.0-beta"
dependencies = [
"bs58",
"chrono",
"clap",
"curve25519-dalek",

10
neveko-auth/Cargo.lock generated
View file

@ -162,15 +162,6 @@ dependencies = [
"generic-array",
]
[[package]]
name = "bs58"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4"
dependencies = [
"tinyvec",
]
[[package]]
name = "build-rs"
version = "0.1.2"
@ -1249,7 +1240,6 @@ dependencies = [
name = "neveko_core"
version = "0.1.0-beta"
dependencies = [
"bs58",
"chrono",
"clap",
"curve25519-dalek",

View file

@ -162,15 +162,6 @@ dependencies = [
"generic-array",
]
[[package]]
name = "bs58"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4"
dependencies = [
"tinyvec",
]
[[package]]
name = "build-rs"
version = "0.1.2"
@ -1249,7 +1240,6 @@ dependencies = [
name = "neveko_core"
version = "0.1.0-beta"
dependencies = [
"bs58",
"chrono",
"clap",
"curve25519-dalek",

10
neveko-core/Cargo.lock generated
View file

@ -162,15 +162,6 @@ dependencies = [
"generic-array",
]
[[package]]
name = "bs58"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4"
dependencies = [
"tinyvec",
]
[[package]]
name = "build-rs"
version = "0.1.2"
@ -1239,7 +1230,6 @@ dependencies = [
name = "neveko_core"
version = "0.1.0-beta"
dependencies = [
"bs58",
"chrono",
"clap",
"curve25519-dalek",

View file

@ -6,7 +6,6 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bs58 = "0.5.1"
chrono = "0.4.23"
clap = { version = "4.1.4", features = ["derive"] }
curve25519-dalek = "4.1.2"

View file

@ -36,6 +36,9 @@ lazy_static! {
/// Current xmr ring size updated here.
const RING_SIZE: u32 = 0x10;
/// Query view key type
const QUERY_TYPE_VIEW_KEY: &str = "view_key";
struct RpcLogin {
username: String,
credential: String,
@ -86,6 +89,7 @@ enum RpcFields {
Make,
Open,
Prepare,
QueryKey,
Refresh,
Sign,
SignMultisig,
@ -121,6 +125,7 @@ impl RpcFields {
RpcFields::Open => String::from("open_wallet"),
RpcFields::Prepare => String::from("prepare_multisig"),
RpcFields::Refresh => String::from("refresh"),
RpcFields::QueryKey => String::from("query_key"),
RpcFields::Sign => String::from("sign"),
RpcFields::SignMultisig => String::from("sign_multisig"),
RpcFields::SubmitMultisig => String::from("submit_multisig"),
@ -1311,6 +1316,7 @@ pub async fn is_multisig() -> reqres::XmrRpcIsMultisigResponse {
}
}
/// Performs the xmr rpc 'get_height' method
pub async fn get_wallet_height() -> reqres::XmrRpcGetHeightResponse {
info!("executing wallet {}", RpcFields::GetHeight.value());
let client = reqwest::Client::new();
@ -1339,6 +1345,38 @@ pub async fn get_wallet_height() -> reqres::XmrRpcGetHeightResponse {
}
}
/// Performs the xmr rpc 'query_key' method
pub async fn query_view_key() -> reqres::XmrRpcQueryKeyResponse {
info!("executing wallet {}", RpcFields::QueryKey.value());
let client = reqwest::Client::new();
let host = get_rpc_host();
let params: reqres::XmrRpcQueryKeyParams = reqres::XmrRpcQueryKeyParams {
key_type: String::from(QUERY_TYPE_VIEW_KEY),
};
let req = reqres::XmrRpcQueryKeyRequest {
jsonrpc: RpcFields::JsonRpcVersion.value(),
id: RpcFields::Id.value(),
method: RpcFields::QueryKey.value(),
params,
};
let login: RpcLogin = get_rpc_creds();
match client
.post(host)
.json(&req)
.send_with_digest_auth(&login.username, &login.credential)
.await
{
Ok(response) => {
let res = response.json::<reqres::XmrRpcQueryKeyResponse>().await;
match res {
Ok(res) => res,
_ => Default::default(),
}
}
Err(_) => Default::default(),
}
}
// Daemon requests
//-------------------------------------------------------------------

View file

@ -1,5 +1,4 @@
use curve25519_dalek::{
constants,
edwards::{
CompressedEdwardsY,
EdwardsPoint,
@ -10,26 +9,38 @@ use num::{
bigint::Sign,
BigInt,
};
use rand_core::{
OsRng,
RngCore,
};
use sha2::{
Digest,
Sha512,
};
use crate::utils;
use crate::{
monero,
utils,
};
#[derive(Debug)]
/// Container for the Neveko Message Keys
pub struct NevekoMessageKeys {
/// Neveko Message Secret Key
pub nmsk: [u8; 32],
/// Neveko Message Public Key
pub nmpk: [u8; 32],
/// Hex encoding of NMSK
pub hex_nmsk: String,
/// Hex encoding of NMPK
pub hex_nmpk: String,
}
/// L value as defined at https://eprint.iacr.org/2008/013.pdf
const CURVE_L: &str = "edd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010";
pub const ENCIPHER: &str = "ENCIPHER";
fn curve_l_as_big_int() -> BigInt {
BigInt::from_bytes_le(Sign::Plus, CURVE_L.as_bytes())
}
fn big_int_to_string(b: &BigInt) -> String {
String::from_utf8(b.to_signed_bytes_le()).unwrap_or(utils::empty_string())
String::from(String::from_utf8(b.to_signed_bytes_le()).unwrap_or_default())
}
/// Hash string input to scalar
@ -62,38 +73,70 @@ fn hash_to_scalar(s: Vec<&str>) -> Scalar {
}
}
/// Convert Monero secret view Key to a Scalar.
fn xmr_svk_to_scalar(svk: String) -> Scalar {
todo!()
}
/// Extract public view key from Monero address to be represented
/// Hash the secret view key to a valid scalar.
///
/// as a CompressedEdwards point.
fn xmr_address_to_pvk_point(address: String) -> CompressedEdwardsY {
todo!()
/// Multiply the NMSK by the ed25519 basepoint to create the
///
/// Neveko Message Public Key.
async fn generate_neveko_message_keys() -> NevekoMessageKeys {
log::info!("generating neveko message keys");
let password = std::env::var(crate::MONERO_WALLET_PASSWORD).unwrap_or(utils::empty_string());
let filename = String::from(crate::APP_NAME);
let m_wallet = monero::open_wallet(&filename, &password).await;
if !m_wallet {
log::error!("failed to open wallet");
}
let svk_res = monero::query_view_key().await;
monero::close_wallet(&filename, &password).await;
let svk = svk_res.result.key;
let scalar_nmsk = hash_to_scalar(vec![&svk[..], crate::APP_NAME]);
let point_nmpk = EdwardsPoint::mul_base(&scalar_nmsk);
let nmsk = scalar_nmsk.as_bytes();
let nmpk: [u8; 32] = *point_nmpk.compress().as_bytes();
let hex_nmpk = hex::encode(&nmpk);
let hex_nmsk = hex::encode(&nmsk);
NevekoMessageKeys {
nmpk,
nmsk: *nmsk,
hex_nmpk,
hex_nmsk,
}
}
/// Encipher a string by using the contact's public view key.
/// Encipher a string by using the contact's Neveko Message Public Key.
///
/// E.g. ss_alice = pvk_bob(address) * svk_alice = h`
///
/// `m = "some message to encipher"`
///
/// Return `x = m + h`
pub fn encipher(address: String, message: String) -> CompressedEdwardsY {
todo!()
}
/// Decipher a string by using the secret view key.
/// Return `x = m + h` as a string of the enciphered message
///
/// E.g. ss_bob = `pvk_alice(address) * svk_bob = h'`
///
/// `m = "some message to decipher"`
///
/// Return `m = x - h'`
pub fn decipher(address: String, message: String) -> CompressedEdwardsY {
todo!()
/// encipher `true` will encipher otherwise decipher
pub async fn cipher(hex_nmpk: &String, message: String, encipher: Option<String>) -> String {
let unwrap_encipher: String = encipher.unwrap_or(utils::empty_string());
let keys: NevekoMessageKeys = generate_neveko_message_keys().await;
log::debug!("neveko keys: {:?}", keys);
// shared secret = pvk * svk
let scalar_svk = Scalar::from_bytes_mod_order(keys.nmsk);
let mut nmpk: [u8; 32] = [0u8; 32];
hex::decode_to_slice(hex_nmpk, &mut nmpk as &mut [u8]).unwrap_or_default();
let compress_y = CompressedEdwardsY::from_slice(&nmpk).unwrap_or_default();
let pvk = compress_y.decompress().unwrap_or_default();
let shared_secret = pvk * scalar_svk;
let ss_hex = hex::encode(shared_secret.compress().as_bytes());
log::debug!("shared_secret: {:?}", ss_hex);
// x = m + h or x = m - h'
let h = hash_to_scalar(vec![&ss_hex[..]]);
let h_bi = BigInt::from_bytes_le(Sign::Plus, h.as_bytes());
if unwrap_encipher == String::from(ENCIPHER) {
let msg_bi = BigInt::from_bytes_le(Sign::Plus, &message.as_bytes());
let x = msg_bi + h_bi;
return hex::encode(x.to_bytes_le().1);
} else {
let msg_bi = BigInt::from_bytes_le(Sign::Plus, &hex::decode(&message).unwrap_or_default());
let x = msg_bi - h_bi;
return big_int_to_string(&x);
};
}
// Tests
@ -103,32 +146,59 @@ pub fn decipher(address: String, message: String) -> CompressedEdwardsY {
mod tests {
use super::*;
fn test_cipher(message: &String, encipher: Option<String>) -> String {
let unwrap_encipher: String = encipher.unwrap_or(utils::empty_string());
let test_nmpk: [u8; 32] = [
203, 2, 188, 13, 167, 96, 59, 189, 38, 238, 2, 71, 84, 155, 153, 73, 241, 137, 9, 30,
28, 134, 91, 137, 134, 73, 231, 45, 174, 98, 103, 158,
];
let nmsk: [u8; 32] = [
54, 55, 48, 48, 99, 48, 101, 52, 102, 99, 99, 56, 54, 56, 50, 50, 52, 101, 101, 55, 51,
48, 102, 54, 54, 57, 101, 97, 54, 100, 101, 0,
];
let hex_nmpk = hex::encode(test_nmpk);
let hex_nmsk = hex::encode(nmsk);
let mut nmpk: [u8; 32] = [0u8; 32];
hex::decode_to_slice(hex_nmpk.clone(), &mut nmpk as &mut [u8]).unwrap_or_default();
assert_eq!(test_nmpk, nmpk);
let keys: NevekoMessageKeys = NevekoMessageKeys {
nmsk,
nmpk,
hex_nmpk,
hex_nmsk,
};
// shared secret = pvk * svk
let scalar_svk = Scalar::from_bytes_mod_order(keys.nmsk);
let compress_y = CompressedEdwardsY::from_slice(&nmpk).unwrap_or_default();
let pvk = compress_y.decompress().unwrap_or_default();
let shared_secret = pvk * scalar_svk;
let ss_hex = hex::encode(shared_secret.compress().as_bytes());
log::debug!("shared_secret: {:?}", ss_hex);
// x = m + h or x = m - h'
let h = hash_to_scalar(vec![&ss_hex[..]]);
let h_bi = BigInt::from_bytes_le(Sign::Plus, h.as_bytes());
if unwrap_encipher == String::from(ENCIPHER) {
let msg_bi = BigInt::from_bytes_le(Sign::Plus, &message.as_bytes());
let x = msg_bi + h_bi;
return hex::encode(x.to_bytes_le().1);
} else {
let msg_bi =
BigInt::from_bytes_le(Sign::Plus, &hex::decode(&message).unwrap_or_default());
let x = msg_bi - h_bi;
return big_int_to_string(&x);
};
}
#[test]
pub fn encipher_decipher() {
let csprng = &mut OsRng;
let mut a_bytes = [0u8; 32];
let mut b_bytes = [0u8; 32];
OsRng::fill_bytes(csprng, &mut a_bytes);
OsRng::fill_bytes(csprng, &mut b_bytes);
let sk_a = Scalar::from_bytes_mod_order(a_bytes);
let sk_b = Scalar::from_bytes_mod_order(b_bytes);
let pk_a = EdwardsPoint::mul_base(&sk_a);
let pk_b = EdwardsPoint::mul_base(&sk_b);
let ss_a = pk_b * sk_a;
let ss_b = pk_a * sk_b;
let h_ss_a = hex::encode(&ss_a.compress().to_bytes());
let h_ss_b = hex::encode(&ss_b.compress().to_bytes());
let msg = "this is a really long message that will be encrypted by the shared secret";
let msg_bi = BigInt::from_bytes_le(Sign::Plus, &msg.as_bytes());
let h = hash_to_scalar(vec![&h_ss_a[..]]);
let h_bi = BigInt::from_bytes_le(Sign::Plus, h.as_bytes());
let x = msg_bi + h_bi;
let x_decoded = big_int_to_string(&x);
assert_ne!(String::from(msg), x_decoded);
let h_prime = hash_to_scalar(vec![&h_ss_b[..]]);
let h_prime_bi = BigInt::from_bytes_le(Sign::Plus, h_prime.as_bytes());
let m_b = x - h_prime_bi;
let decoded = big_int_to_string(&m_b);
assert_eq!(String::from(msg), decoded);
let message = String::from(
"This is message that will be encrypted by the network.
it is really long for testing and breaking stuff",
);
let do_encipher = Some(String::from(ENCIPHER));
let encipher = test_cipher(&message, do_encipher);
assert_ne!(encipher, message);
let decipher = test_cipher(&encipher, None);
assert_eq!(decipher, message);
}
}

View file

@ -157,6 +157,11 @@ pub struct XmrRpcChangePasswordParams {
pub new_password: String,
}
#[derive(Deserialize, Serialize, Debug)]
pub struct XmrRpcQueryKeyParams {
pub key_type: String,
}
// requests
#[derive(Deserialize, Serialize, Debug)]
pub struct XmrRpcValidateAddressRequest {
@ -331,6 +336,14 @@ pub struct XmrRpcChangePasswordRequest {
pub params: XmrRpcChangePasswordParams,
}
#[derive(Deserialize, Serialize, Debug)]
pub struct XmrRpcQueryKeyRequest {
pub jsonrpc: String,
pub id: String,
pub method: String,
pub params: XmrRpcQueryKeyParams,
}
// results
#[derive(Deserialize, Debug)]
pub struct XmrRpcValidateAddressResult {
@ -558,6 +571,11 @@ pub struct XmrRpcGetHeightResult {
pub height: u64,
}
#[derive(Deserialize, Debug)]
pub struct XmrRpcQueryKeyResult {
pub key: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct XmrDaemonGetInfoResult {
pub adjusted_time: u64,
@ -1169,6 +1187,21 @@ impl Default for XmrRpcGetHeightResponse {
}
}
}
#[derive(Deserialize, Debug)]
pub struct XmrRpcQueryKeyResponse {
pub result: XmrRpcQueryKeyResult,
}
impl Default for XmrRpcQueryKeyResponse {
fn default() -> Self {
XmrRpcQueryKeyResponse {
result: XmrRpcQueryKeyResult {
key: utils::empty_string(),
},
}
}
}
// END XMR Structs
/// Container for the message decryption

10
neveko-gui/Cargo.lock generated
View file

@ -388,15 +388,6 @@ dependencies = [
"objc2-encode",
]
[[package]]
name = "bs58"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4"
dependencies = [
"tinyvec",
]
[[package]]
name = "build-rs"
version = "0.1.2"
@ -2459,7 +2450,6 @@ dependencies = [
name = "neveko_core"
version = "0.1.0-beta"
dependencies = [
"bs58",
"chrono",
"clap",
"curve25519-dalek",

View file

@ -167,15 +167,6 @@ dependencies = [
"generic-array",
]
[[package]]
name = "bs58"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4"
dependencies = [
"tinyvec",
]
[[package]]
name = "build-rs"
version = "0.1.2"
@ -1288,7 +1279,6 @@ dependencies = [
name = "neveko_core"
version = "0.1.0-beta"
dependencies = [
"bs58",
"chrono",
"clap",
"curve25519-dalek",

View file

@ -162,15 +162,6 @@ dependencies = [
"generic-array",
]
[[package]]
name = "bs58"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4"
dependencies = [
"tinyvec",
]
[[package]]
name = "build-rs"
version = "0.1.2"
@ -1239,7 +1230,6 @@ dependencies = [
name = "neveko_core"
version = "0.1.0-beta"
dependencies = [
"bs58",
"chrono",
"clap",
"curve25519-dalek",