From 854fca3806aff228881f85528d4cc144637e1d3a Mon Sep 17 00:00:00 2001 From: Luke Parker Date: Sat, 9 Jul 2022 21:51:39 -0400 Subject: [PATCH] Close https://github.com/serai-dex/serai/issues/30. An extremely minimal subset of Monero is now all that's built, and I'm sufficiently happy with it. --- coins/monero/Cargo.toml | 9 ++- coins/monero/build.rs | 110 +++++++++++++------------------------ coins/monero/c/wrapper.cpp | 70 ++++++++++++++++------- coins/monero/src/lib.rs | 27 +++++++++ processor/build.rs | 3 - 5 files changed, 121 insertions(+), 98 deletions(-) delete mode 100644 processor/build.rs diff --git a/coins/monero/Cargo.toml b/coins/monero/Cargo.toml index f62145a7..f262a3ae 100644 --- a/coins/monero/Cargo.toml +++ b/coins/monero/Cargo.toml @@ -19,6 +19,8 @@ rand_chacha = { version = "0.3", optional = true } rand = "0.8" rand_distr = "0.4" +subtle = "2.4" + tiny-keccak = { version = "2", features = ["keccak"] } blake2 = { version = "0.10", optional = true } @@ -31,13 +33,14 @@ transcript = { package = "flexible-transcript", path = "../../crypto/transcript" frost = { package = "modular-frost", path = "../../crypto/frost", features = ["ed25519"], optional = true } dleq = { path = "../../crypto/dleq", features = ["serialize"], optional = true } -base58-monero = "1" -monero = "0.16" - hex = "0.4" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" + +base58-monero = "1" monero-epee-bin-serde = "1.0" +monero = "0.16" + reqwest = { version = "0.11", features = ["json"] } [features] diff --git a/coins/monero/build.rs b/coins/monero/build.rs index d2b6f66d..dc5943b1 100644 --- a/coins/monero/build.rs +++ b/coins/monero/build.rs @@ -14,7 +14,6 @@ fn main() { // Use a file to signal if Monero was already built, as that should never be rebuilt // If the signaling file was deleted, run this script again to rebuild Monero though - // TODO: Move this signaling file into OUT_DIR once Monero is built statically successfully println!("cargo:rerun-if-changed=c/.build/monero"); if !Path::new("c/.build/monero").exists() { if !Command::new("make").arg(format!("-j{}", &env::var("THREADS").unwrap_or("2".to_string()))) @@ -28,81 +27,46 @@ fn main() { } } - println!("cargo:rerun-if-env-changed=OUT_DIR"); - if !Path::new( - &format!( - "{}/{}cncrypto.{}", - out_dir, - &env::consts::DLL_PREFIX, - &env::consts::DLL_EXTENSION - ) - ).exists() { - let mut paths = vec![ - "c/monero/build/release/contrib/epee/src/libepee.a".to_string(), - "c/monero/build/release/external/easylogging++/libeasylogging.a".to_string(), - "c/monero/build/release/external/randomx/librandomx.a".to_string() - ]; - - for (folder, lib) in [ - ("common", "common"), - ("crypto", "cncrypto"), - ("crypto/wallet", "wallet-crypto"), - ("cryptonote_basic", "cryptonote_basic"), - ("cryptonote_basic", "cryptonote_format_utils_basic"), - ("", "version"), - ("device", "device"), - ("ringct", "ringct_basic"), - ("ringct", "ringct") - ] { - paths.push( - format!( - "c/monero/build/release/src/{}/{}{}.a", - folder, - &env::consts::DLL_PREFIX, - lib - ) - ); - } - - for path in paths { - if !Command::new("cp").args(&[&path, out_dir]).status().unwrap().success() { - panic!("Failed to cp {}", path); - } - } - } - println!("cargo:rerun-if-changed=c/wrapper.cpp"); - if !Path::new(&format!("{}/{}wrapper.a", out_dir, &env::consts::DLL_PREFIX)).exists() { - cc::Build::new() - .file("c/wrapper.cpp") - .cpp(true) - .warnings(false) - .include("c/monero/contrib/epee/include") - .include("c/monero/src") - .compile("wrapper"); - } + cc::Build::new() + .static_flag(true) + .warnings(false) + .extra_warnings(false) + .flag("-Wno-deprecated-declarations") + + .include("c/monero/external/supercop/include") + .include("c/monero/contrib/epee/include") + .include("c/monero/src") + .include("c/monero/build/release/generated_include") + + .define("AUTO_INITIALIZE_EASYLOGGINGPP", None) + .include("c/monero/external/easylogging++") + .file("c/monero/external/easylogging++/easylogging++.cc") + + .file("c/monero/src/common/aligned.c") + .file("c/monero/src/common/perf_timer.cpp") + + .include("c/monero/src/crypto") + .file("c/monero/src/crypto/crypto-ops-data.c") + .file("c/monero/src/crypto/crypto-ops.c") + .file("c/monero/src/crypto/keccak.c") + .file("c/monero/src/crypto/hash.c") + + .include("c/monero/src/device") + .file("c/monero/src/device/device_default.cpp") + + .include("c/monero/src/ringct") + .file("c/monero/src/ringct/rctCryptoOps.c") + .file("c/monero/src/ringct/rctTypes.cpp") + .file("c/monero/src/ringct/rctOps.cpp") + .file("c/monero/src/ringct/multiexp.cc") + .file("c/monero/src/ringct/bulletproofs.cc") + .file("c/monero/src/ringct/rctSigs.cpp") + + .file("c/wrapper.cpp") + .compile("wrapper"); println!("cargo:rustc-link-search={}", out_dir); println!("cargo:rustc-link-lib=wrapper"); - println!("cargo:rustc-link-lib=ringct"); - println!("cargo:rustc-link-lib=ringct_basic"); - println!("cargo:rustc-link-lib=device"); - println!("cargo:rustc-link-lib=cryptonote_basic"); - println!("cargo:rustc-link-lib=cncrypto"); - println!("cargo:rustc-link-lib=cryptonote_format_utils_basic"); - println!("cargo:rustc-link-lib=version"); - println!("cargo:rustc-link-lib=wallet-crypto"); - println!("cargo:rustc-link-lib=easylogging"); - println!("cargo:rustc-link-lib=epee"); - println!("cargo:rustc-link-lib=common"); - println!("cargo:rustc-link-lib=randomx"); - println!("cargo:rustc-link-lib=unbound"); - println!("cargo:rustc-link-lib=sodium"); - println!("cargo:rustc-link-lib=boost_system"); - println!("cargo:rustc-link-lib=boost_thread"); - println!("cargo:rustc-link-lib=boost_filesystem"); - println!("cargo:rustc-link-lib=hidapi-hidraw"); println!("cargo:rustc-link-lib=stdc++"); - - println!("cargo:rustc-link-arg=-zmuldefs"); } diff --git a/coins/monero/c/wrapper.cpp b/coins/monero/c/wrapper.cpp index e99a363a..7ed31ac8 100644 --- a/coins/monero/c/wrapper.cpp +++ b/coins/monero/c/wrapper.cpp @@ -6,36 +6,50 @@ #include "ringct/rctSigs.h" typedef std::lock_guard lock; -std::mutex rng_mutex; +std::mutex rng_mutex; uint8_t rng_entropy[64]; -void rng(uint8_t* seed) { - // Set the first half to the seed - memcpy(rng_entropy, seed, 32); - // Set the second half to the hash of a DST to ensure a lack of collisions - crypto::cn_fast_hash("RNG_entropy_seed", 16, (char*) &rng_entropy[32]); -} extern "C" { - void generate_random_bytes_not_thread_safe(size_t n, uint8_t* value) { + void rng(uint8_t* seed) { + // Set the first half to the seed + memcpy(rng_entropy, seed, 32); + // Set the second half to the hash of a DST to ensure a lack of collisions + crypto::cn_fast_hash("RNG_entropy_seed", 16, (char*) &rng_entropy[32]); + } +} + +extern "C" void monero_wide_reduce(uint8_t* value); +namespace crypto { + void generate_random_bytes_not_thread_safe(size_t n, void* value) { size_t written = 0; while (written != n) { uint8_t hash[32]; crypto::cn_fast_hash(rng_entropy, 64, (char*) hash); // Step the RNG by setting the latter half to the most recent result - // Does not leak the RNG, even if the values are leaked (which they are expected to be) due to - // the first half remaining constant and undisclosed + // Does not leak the RNG, even if the values are leaked (which they are + // expected to be) due to the first half remaining constant and + // undisclosed memcpy(&rng_entropy[32], hash, 32); size_t next = n - written; if (next > 32) { next = 32; } - memcpy(&value[written], hash, next); + memcpy(&((uint8_t*) value)[written], hash, next); written += next; } } + void random32_unbiased(unsigned char *bytes) { + uint8_t value[64]; + generate_random_bytes_not_thread_safe(64, value); + monero_wide_reduce(value); + memcpy(bytes, value, 32); + } +} + +extern "C" { void c_hash_to_point(uint8_t* point) { rct::key key_point; ge_p3 e_p3; @@ -62,16 +76,24 @@ extern "C" { std::stringstream ss; binary_archive ba(ss); ::serialization::serialize(ba, bp); - uint8_t* res = (uint8_t*) calloc(ss.str().size(), 1); // malloc would also work + uint8_t* res = (uint8_t*) calloc(ss.str().size(), 1); memcpy(res, ss.str().data(), ss.str().size()); return res; } - bool c_verify_bp(uint8_t* seed, uint s_len, uint8_t* s, uint8_t c_len, uint8_t* c) { - // BPs are batch verified which use RNG based challenges to ensure individual integrity - // That's why this must also have control over RNG, to prevent interrupting multisig signing - // while not using known seeds. Considering this doesn't actually define a batch, - // and it's only verifying a single BP, it'd probably be fine, but... + bool c_verify_bp( + uint8_t* seed, + uint s_len, + uint8_t* s, + uint8_t c_len, + uint8_t* c + ) { + // BPs are batch verified which use RNG based weights to ensure individual + // integrity + // That's why this must also have control over RNG, to prevent interrupting + // multisig signing while not using known seeds. Considering this doesn't + // actually define a batch, and it's only verifying a single BP, + // it'd probably be fine, but... lock guard(rng_mutex); rng(seed); @@ -94,7 +116,15 @@ extern "C" { try { return rct::bulletproof_VERIFY(bp); } catch(...) { return false; } } - bool c_verify_clsag(uint s_len, uint8_t* s, uint8_t k_len, uint8_t* k, uint8_t* I, uint8_t* p, uint8_t* m) { + bool c_verify_clsag( + uint s_len, + uint8_t* s, + uint8_t k_len, + uint8_t* k, + uint8_t* I, + uint8_t* p, + uint8_t* m + ) { rct::clsag clsag; std::stringstream ss; std::string str; @@ -121,6 +151,8 @@ extern "C" { rct::key msg; memcpy(msg.bytes, m, 32); - try { return verRctCLSAGSimple(msg, clsag, keys, pseudo_out); } catch(...) { return false; } + try { + return verRctCLSAGSimple(msg, clsag, keys, pseudo_out); + } catch(...) { return false; } } } diff --git a/coins/monero/src/lib.rs b/coins/monero/src/lib.rs index 7425282d..8237b4f6 100644 --- a/coins/monero/src/lib.rs +++ b/coins/monero/src/lib.rs @@ -1,6 +1,10 @@ +use std::slice; + use lazy_static::lazy_static; use rand_core::{RngCore, CryptoRng}; +use subtle::ConstantTimeEq; + use tiny_keccak::{Hasher, Keccak}; use curve25519_dalek::{ @@ -32,6 +36,29 @@ lazy_static! { static ref H_TABLE: EdwardsBasepointTable = EdwardsBasepointTable::create(&*H); } +// Function from libsodium our subsection of Monero relies on. Implementing it here means we don't +// need to link against libsodium +#[no_mangle] +unsafe extern "C" fn crypto_verify_32(a: *const u8, b: *const u8) -> isize { + isize::from( + slice::from_raw_parts(a, 32).ct_eq(slice::from_raw_parts(b, 32)).unwrap_u8() + ) - 1 +} + +// Offer a wide reduction to C. Our seeded RNG prevented Monero from defining an unbiased scalar +// generation function, and in order to not use Monero code (which would require propagating its +// license), the function was rewritten. It was rewritten with wide reduction, instead of rejection +// sampling however, hence the need for this function +#[no_mangle] +unsafe extern "C" fn monero_wide_reduce(value: *mut u8) { + let res = Scalar::from_bytes_mod_order_wide( + std::slice::from_raw_parts(value, 64).try_into().unwrap() + ); + for (i, b) in res.to_bytes().iter().enumerate() { + value.add(i).write(*b); + } +} + #[allow(non_snake_case)] #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub struct Commitment { diff --git a/processor/build.rs b/processor/build.rs deleted file mode 100644 index a8fb5e40..00000000 --- a/processor/build.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("cargo:rustc-link-arg=-zmuldefs"); -}