diff --git a/Cargo.lock b/Cargo.lock index 8d406fe1..7e3700fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4810,12 +4810,24 @@ dependencies = [ ] [[package]] -name = "monero-primitives" +name = "monero-mlsag" version = "0.1.0" dependencies = [ "curve25519-dalek", "monero-generators", "monero-io", + "monero-primitives", + "std-shims", + "thiserror", + "zeroize", +] + +[[package]] +name = "monero-primitives" +version = "0.1.0" +dependencies = [ + "curve25519-dalek", + "monero-generators", "sha3", "std-shims", "zeroize", @@ -4839,8 +4851,8 @@ dependencies = [ "monero-clsag", "monero-generators", "monero-io", + "monero-mlsag", "monero-primitives", - "multiexp", "pbkdf2 0.12.2", "rand", "rand_chacha", @@ -8100,6 +8112,7 @@ dependencies = [ "monero-clsag", "monero-generators", "monero-io", + "monero-mlsag", "monero-primitives", "monero-serai", "multiexp", diff --git a/Cargo.toml b/Cargo.toml index 1e8da216..b6973c4b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,7 @@ members = [ "coins/monero/io", "coins/monero/generators", "coins/monero/primitives", + "coins/monero/ringct/mlsag", "coins/monero/ringct/clsag", "coins/monero/ringct/bulletproofs", "coins/monero", diff --git a/coins/monero/Cargo.toml b/coins/monero/Cargo.toml index 608963f2..d2a9c7c7 100644 --- a/coins/monero/Cargo.toml +++ b/coins/monero/Cargo.toml @@ -39,7 +39,6 @@ curve25519-dalek = { version = "4", default-features = false, features = ["alloc # Used for the hash to curve, along with the more complicated proofs group = { version = "0.13", default-features = false } dalek-ff-group = { path = "../../crypto/dalek-ff-group", version = "0.4", default-features = false } -multiexp = { path = "../../crypto/multiexp", version = "0.4", default-features = false, features = ["batch"] } # Needed for multisig transcript = { package = "flexible-transcript", path = "../../crypto/transcript", version = "0.3", default-features = false, features = ["recommended"], optional = true } @@ -48,6 +47,7 @@ frost = { package = "modular-frost", path = "../../crypto/frost", version = "0.8 monero-io = { path = "io", version = "0.1", default-features = false } monero-generators = { path = "generators", version = "0.4", default-features = false } monero-primitives = { path = "primitives", version = "0.1", default-features = false } +monero-mlsag = { path = "ringct/mlsag", version = "0.1", default-features = false } monero-clsag = { path = "ringct/clsag", version = "0.1", default-features = false } monero-bulletproofs = { path = "ringct/bulletproofs", version = "0.1", default-features = false } @@ -89,13 +89,12 @@ std = [ "sha3/std", "pbkdf2/std", - "multiexp/std", - "transcript/std", "monero-io/std", "monero-generators/std", "monero-primitives/std", + "monero-mlsag/std", "monero-clsag/std", "monero-bulletproofs/std", diff --git a/coins/monero/primitives/Cargo.toml b/coins/monero/primitives/Cargo.toml index ec6d13e3..6184bf5c 100644 --- a/coins/monero/primitives/Cargo.toml +++ b/coins/monero/primitives/Cargo.toml @@ -25,7 +25,6 @@ sha3 = { version = "0.10", default-features = false } curve25519-dalek = { version = "4", default-features = false, features = ["alloc", "zeroize"] } # Other Monero dependencies -monero-io = { path = "../io", version = "0.1", default-features = false } monero-generators = { path = "../generators", version = "0.4", default-features = false } [features] diff --git a/coins/monero/ringct/mlsag/Cargo.toml b/coins/monero/ringct/mlsag/Cargo.toml new file mode 100644 index 00000000..e8a9747f --- /dev/null +++ b/coins/monero/ringct/mlsag/Cargo.toml @@ -0,0 +1,45 @@ +[package] +name = "monero-mlsag" +version = "0.1.0" +description = "The MLSAG linkable ring signature, as defined by the Monero protocol" +license = "MIT" +repository = "https://github.com/serai-dex/serai/tree/develop/coins/monero/ringct/mlsag" +authors = ["Luke Parker "] +edition = "2021" +rust-version = "1.79" + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[lints] +workspace = true + +[dependencies] +std-shims = { path = "../../../../common/std-shims", version = "^0.1.1", default-features = false } + +thiserror = { version = "1", default-features = false, optional = true } + +zeroize = { version = "^1.5", default-features = false, features = ["zeroize_derive"] } + +# Cryptographic dependencies +curve25519-dalek = { version = "4", default-features = false, features = ["alloc", "zeroize"] } + +# Other Monero dependencies +monero-io = { path = "../../io", version = "0.1", default-features = false } +monero-generators = { path = "../../generators", version = "0.4", default-features = false } +monero-primitives = { path = "../../primitives", version = "0.1", default-features = false } + +[features] +std = [ + "std-shims/std", + + "thiserror", + + "zeroize/std", + + "monero-io/std", + "monero-generators/std", + "monero-primitives/std", +] +default = ["std"] diff --git a/coins/monero/ringct/mlsag/LICENSE b/coins/monero/ringct/mlsag/LICENSE new file mode 100644 index 00000000..91d893c1 --- /dev/null +++ b/coins/monero/ringct/mlsag/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022-2024 Luke Parker + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/coins/monero/ringct/mlsag/README.md b/coins/monero/ringct/mlsag/README.md new file mode 100644 index 00000000..895d7116 --- /dev/null +++ b/coins/monero/ringct/mlsag/README.md @@ -0,0 +1,6 @@ +# Monero MLSAG + +The MLSAG linkable ring signature, as defined by the Monero protocol. + +This library is usable under no-std when the `std` feature (on by default) is +disabled. diff --git a/coins/monero/ringct/mlsag/src/lib.rs b/coins/monero/ringct/mlsag/src/lib.rs index afee8d5f..deae941f 100644 --- a/coins/monero/ringct/mlsag/src/lib.rs +++ b/coins/monero/ringct/mlsag/src/lib.rs @@ -1,3 +1,9 @@ +#![cfg_attr(docsrs, feature(doc_auto_cfg))] +#![doc = include_str!("../README.md")] +#![deny(missing_docs)] +#![cfg_attr(not(feature = "std"), no_std)] +#![allow(non_snake_case)] + use std_shims::{ vec::Vec, io::{self, Read, Write}, @@ -7,9 +13,9 @@ use zeroize::Zeroize; use curve25519_dalek::{traits::IsIdentity, Scalar, EdwardsPoint}; -use monero_generators::H; - -use crate::{hash_to_scalar, ringct::hash_to_point, serialize::*}; +use monero_io::*; +use monero_generators::{H, hash_to_point}; +use monero_primitives::keccak256_to_scalar; /// Errors when working with MLSAGs. #[derive(Clone, Copy, PartialEq, Eq, Debug)] @@ -68,17 +74,17 @@ impl RingMatrix { RingMatrix::new(matrix) } - /// Iterate the members of the matrix. + /// Iterate over the members of the matrix. fn iter(&self) -> impl Iterator { self.matrix.iter().map(AsRef::as_ref) } - /// Returns the amount of members in the ring. + /// Get the amount of members in the ring. pub fn members(&self) -> usize { self.matrix.len() } - /// Returns the length of a ring member. + /// Get the length of a ring member. /// /// A ring member is a vector of points for which the signer knows all of the discrete logarithms /// of. @@ -96,7 +102,7 @@ pub struct Mlsag { } impl Mlsag { - /// Write the MLSAG to a writer. + /// Write a MLSAG. pub fn write(&self, w: &mut W) -> io::Result<()> { for ss in &self.ss { write_raw_vec(write_scalar, ss, w)?; @@ -104,7 +110,7 @@ impl Mlsag { write_scalar(&self.cc, w) } - /// Read the MLSAG from a reader. + /// Read a MLSAG. pub fn read(mixins: usize, ss_2_elements: usize, r: &mut R) -> io::Result { Ok(Mlsag { ss: (0 .. mixins) @@ -114,7 +120,7 @@ impl Mlsag { }) } - /// Verify the MLSAG. + /// Verify a MLSAG. pub fn verify( &self, msg: &[u8; 32], @@ -149,7 +155,8 @@ impl Mlsag { #[allow(non_snake_case)] let L = EdwardsPoint::vartime_double_scalar_mul_basepoint(&ci, ring_member_entry, s); - buf.extend_from_slice(ring_member_entry.compress().as_bytes()); + let compressed_ring_member_entry = ring_member_entry.compress(); + buf.extend_from_slice(compressed_ring_member_entry.as_bytes()); buf.extend_from_slice(L.compress().as_bytes()); // Not all dimensions need to be linkable, e.g. commitments, and only linkable layers need @@ -160,12 +167,12 @@ impl Mlsag { } #[allow(non_snake_case)] - let R = (s * hash_to_point(ring_member_entry)) + (ci * ki); + let R = (s * hash_to_point(compressed_ring_member_entry.to_bytes())) + (ci * ki); buf.extend_from_slice(R.compress().as_bytes()); } } - ci = hash_to_scalar(&buf); + ci = keccak256_to_scalar(&buf); // keep the msg in the buffer. buf.drain(msg.len() ..); } @@ -190,7 +197,7 @@ pub struct AggregateRingMatrixBuilder { impl AggregateRingMatrixBuilder { /// Create a new AggregateRingMatrixBuilder. /// - /// Takes in the transaction's outputs; commitments and fee. + /// This takes in the transaction's outputs' commitments and fee used. pub fn new(commitments: &[EdwardsPoint], fee: u64) -> Self { AggregateRingMatrixBuilder { key_ring: vec![], diff --git a/coins/monero/src/ringct/mod.rs b/coins/monero/src/ringct/mod.rs index 8256d76f..6192ba6b 100644 --- a/coins/monero/src/ringct/mod.rs +++ b/coins/monero/src/ringct/mod.rs @@ -12,7 +12,7 @@ pub(crate) mod hash_to_point; pub use hash_to_point::{raw_hash_to_point, hash_to_point}; /// MLSAG struct, along with verifying functionality. -pub mod mlsag; +pub use monero_mlsag as mlsag; /// CLSAG struct, along with signing and verifying functionality. pub use monero_clsag as clsag; /// BorromeanRange struct, along with verifying functionality. diff --git a/tests/no-std/Cargo.toml b/tests/no-std/Cargo.toml index daad3577..2c325857 100644 --- a/tests/no-std/Cargo.toml +++ b/tests/no-std/Cargo.toml @@ -38,6 +38,7 @@ bitcoin-serai = { path = "../../coins/bitcoin", default-features = false, featur monero-io = { path = "../../coins/monero/io", default-features = false } monero-generators = { path = "../../coins/monero/generators", default-features = false } monero-primitives = { path = "../../coins/monero/primitives", default-features = false } +monero-mlsag = { path = "../../coins/monero/ringct/mlsag", default-features = false } monero-clsag = { path = "../../coins/monero/ringct/clsag", default-features = false } monero-bulletproofs = { path = "../../coins/monero/ringct/bulletproofs", default-features = false } monero-serai = { path = "../../coins/monero", default-features = false } diff --git a/tests/no-std/src/lib.rs b/tests/no-std/src/lib.rs index 183fd40e..a21189ab 100644 --- a/tests/no-std/src/lib.rs +++ b/tests/no-std/src/lib.rs @@ -20,5 +20,10 @@ pub use frost_schnorrkel; pub use bitcoin_serai; +pub use monero_io; pub use monero_generators; +pub use monero_primitives; +pub use monero_mlsag; +pub use monero_clsag; +pub use monero_bulletproofs; pub use monero_serai;