mirror of
https://github.com/serai-dex/serai.git
synced 2025-01-23 19:15:59 +00:00
Add Rust CLSAG verification
Marked experimental, not guaranteed to match Monero yet
This commit is contained in:
parent
bb840da44d
commit
3533e66c7f
6 changed files with 181 additions and 95 deletions
|
@ -35,6 +35,7 @@ monero-epee-bin-serde = "1.0"
|
||||||
reqwest = { version = "0.11", features = ["json"] }
|
reqwest = { version = "0.11", features = ["json"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
experimental = []
|
||||||
multisig = ["ff", "group", "rand_chacha", "transcript", "frost", "dalek-ff-group"]
|
multisig = ["ff", "group", "rand_chacha", "transcript", "frost", "dalek-ff-group"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
|
@ -53,7 +53,7 @@ extern "C" {
|
||||||
try { return rct::bulletproof_VERIFY(bp); } catch(...) { return false; }
|
try { return rct::bulletproof_VERIFY(bp); } catch(...) { return false; }
|
||||||
}
|
}
|
||||||
|
|
||||||
bool c_verify_clsag(uint s_len, uint8_t* s, uint8_t* I, uint8_t k_len, uint8_t* k, 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;
|
rct::clsag clsag;
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
std::string str;
|
std::string str;
|
||||||
|
@ -64,7 +64,6 @@ extern "C" {
|
||||||
if (!ss.good()) {
|
if (!ss.good()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
memcpy(clsag.I.bytes, I, 32);
|
|
||||||
|
|
||||||
rct::ctkeyV keys;
|
rct::ctkeyV keys;
|
||||||
keys.resize(k_len);
|
keys.resize(k_len);
|
||||||
|
@ -73,6 +72,8 @@ extern "C" {
|
||||||
memcpy(keys[i].mask.bytes, &k[((i * 2) + 1) * 32], 32);
|
memcpy(keys[i].mask.bytes, &k[((i * 2) + 1) * 32], 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memcpy(clsag.I.bytes, I, 32);
|
||||||
|
|
||||||
rct::key pseudo_out;
|
rct::key pseudo_out;
|
||||||
memcpy(pseudo_out.bytes, p, 32);
|
memcpy(pseudo_out.bytes, p, 32);
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
|
use lazy_static::lazy_static;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use rand_core::{RngCore, CryptoRng};
|
use rand_core::{RngCore, CryptoRng};
|
||||||
|
|
||||||
|
@ -7,6 +10,8 @@ use curve25519_dalek::{
|
||||||
traits::VartimePrecomputedMultiscalarMul,
|
traits::VartimePrecomputedMultiscalarMul,
|
||||||
edwards::{EdwardsPoint, VartimeEdwardsPrecomputation}
|
edwards::{EdwardsPoint, VartimeEdwardsPrecomputation}
|
||||||
};
|
};
|
||||||
|
#[cfg(feature = "experimental")]
|
||||||
|
use curve25519_dalek::edwards::CompressedEdwardsY;
|
||||||
|
|
||||||
use monero::{consensus::Encodable, util::ringct::{Key, Clsag}};
|
use monero::{consensus::Encodable, util::ringct::{Key, Clsag}};
|
||||||
|
|
||||||
|
@ -15,8 +20,7 @@ use crate::{
|
||||||
transaction::decoys::Decoys,
|
transaction::decoys::Decoys,
|
||||||
random_scalar,
|
random_scalar,
|
||||||
hash_to_scalar,
|
hash_to_scalar,
|
||||||
hash_to_point,
|
hash_to_point
|
||||||
c_verify_clsag
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "multisig")]
|
#[cfg(feature = "multisig")]
|
||||||
|
@ -31,7 +35,13 @@ pub enum Error {
|
||||||
#[error("invalid ring member (member {0}, ring size {1})")]
|
#[error("invalid ring member (member {0}, ring size {1})")]
|
||||||
InvalidRingMember(u8, u8),
|
InvalidRingMember(u8, u8),
|
||||||
#[error("invalid commitment")]
|
#[error("invalid commitment")]
|
||||||
InvalidCommitment
|
InvalidCommitment,
|
||||||
|
#[error("invalid D")]
|
||||||
|
InvalidD,
|
||||||
|
#[error("invalid s")]
|
||||||
|
InvalidS,
|
||||||
|
#[error("invalid c1")]
|
||||||
|
InvalidC1
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -42,6 +52,10 @@ pub struct Input {
|
||||||
pub decoys: Decoys
|
pub decoys: Decoys
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref INV_EIGHT: Scalar = Scalar::from(8 as u8).invert();
|
||||||
|
}
|
||||||
|
|
||||||
impl Input {
|
impl Input {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
commitment: Commitment,
|
commitment: Commitment,
|
||||||
|
@ -64,94 +78,98 @@ impl Input {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
enum Mode {
|
||||||
pub(crate) fn sign_core<R: RngCore + CryptoRng>(
|
Sign(usize, EdwardsPoint, EdwardsPoint),
|
||||||
rng: &mut R,
|
#[cfg(feature = "experimental")]
|
||||||
image: &EdwardsPoint,
|
Verify(Scalar)
|
||||||
input: &Input,
|
}
|
||||||
mask: Scalar,
|
|
||||||
|
fn core(
|
||||||
|
ring: &[[EdwardsPoint; 2]],
|
||||||
|
I: &EdwardsPoint,
|
||||||
|
pseudo_out: &EdwardsPoint,
|
||||||
msg: &[u8; 32],
|
msg: &[u8; 32],
|
||||||
A: EdwardsPoint,
|
D: &EdwardsPoint,
|
||||||
AH: EdwardsPoint
|
s: &[Scalar],
|
||||||
) -> (Clsag, Scalar, Scalar, Scalar, Scalar, EdwardsPoint) {
|
// Use a Result as Either for sign/verify
|
||||||
let n = input.decoys.len();
|
A_c1: Mode
|
||||||
let r: usize = input.decoys.i.into();
|
) -> (([u8; 32], Scalar, Scalar), Scalar) {
|
||||||
|
let n = ring.len();
|
||||||
let C_out;
|
|
||||||
|
|
||||||
let mut P = vec![];
|
|
||||||
P.reserve_exact(n);
|
|
||||||
let mut C = vec![];
|
|
||||||
C.reserve_exact(n);
|
|
||||||
let mut C_non_zero = vec![];
|
|
||||||
C_non_zero.reserve_exact(n);
|
|
||||||
|
|
||||||
let z;
|
|
||||||
|
|
||||||
{
|
|
||||||
C_out = Commitment::new(mask, input.commitment.amount).calculate();
|
|
||||||
|
|
||||||
for member in &input.decoys.ring {
|
|
||||||
P.push(member[0]);
|
|
||||||
C_non_zero.push(member[1]);
|
|
||||||
C.push(C_non_zero[C_non_zero.len() - 1] - C_out);
|
|
||||||
}
|
|
||||||
|
|
||||||
z = input.commitment.mask - mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
let H = hash_to_point(&P[r]);
|
|
||||||
let mut D = H * z;
|
|
||||||
|
|
||||||
// Doesn't use a constant time table as dalek takes longer to generate those then they save
|
// Doesn't use a constant time table as dalek takes longer to generate those then they save
|
||||||
let images_precomp = VartimeEdwardsPrecomputation::new([image, &D]);
|
let images_precomp = VartimeEdwardsPrecomputation::new([I, D]);
|
||||||
D = Scalar::from(8 as u8).invert() * D;
|
let D = D * *INV_EIGHT;
|
||||||
|
|
||||||
let mut to_hash = vec![];
|
let mut to_hash = vec![];
|
||||||
to_hash.reserve_exact(((2 * n) + 4) * 32);
|
to_hash.reserve_exact(((2 * n) + 5) * 32);
|
||||||
const PREFIX: &str = "CLSAG_";
|
const PREFIX: &str = "CLSAG_";
|
||||||
const AGG_0: &str = "CLSAG_agg_0";
|
const AGG_0: &str = "CLSAG_agg_0";
|
||||||
const ROUND: &str = "round";
|
const ROUND: &str = "round";
|
||||||
to_hash.extend(AGG_0.bytes());
|
to_hash.extend(AGG_0.bytes());
|
||||||
to_hash.extend([0; 32 - AGG_0.len()]);
|
to_hash.extend([0; 32 - AGG_0.len()]);
|
||||||
|
|
||||||
for i in 0 .. n {
|
let mut P = vec![];
|
||||||
to_hash.extend(P[i].compress().to_bytes());
|
P.reserve_exact(n);
|
||||||
|
let mut C = vec![];
|
||||||
|
C.reserve_exact(n);
|
||||||
|
for member in ring {
|
||||||
|
P.push(member[0]);
|
||||||
|
C.push(member[1] - pseudo_out);
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in 0 .. n {
|
for member in ring {
|
||||||
to_hash.extend(C_non_zero[i].compress().to_bytes());
|
to_hash.extend(member[0].compress().to_bytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
to_hash.extend(image.compress().to_bytes());
|
for member in ring {
|
||||||
|
to_hash.extend(member[1].compress().to_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
to_hash.extend(I.compress().to_bytes());
|
||||||
let D_bytes = D.compress().to_bytes();
|
let D_bytes = D.compress().to_bytes();
|
||||||
to_hash.extend(D_bytes);
|
to_hash.extend(D_bytes);
|
||||||
to_hash.extend(C_out.compress().to_bytes());
|
to_hash.extend(pseudo_out.compress().to_bytes());
|
||||||
let mu_P = hash_to_scalar(&to_hash);
|
let mu_P = hash_to_scalar(&to_hash);
|
||||||
to_hash[AGG_0.len() - 1] = '1' as u8;
|
to_hash[AGG_0.len() - 1] = '1' as u8;
|
||||||
let mu_C = hash_to_scalar(&to_hash);
|
let mu_C = hash_to_scalar(&to_hash);
|
||||||
|
|
||||||
to_hash.truncate(((2 * n) + 1) * 32);
|
to_hash.truncate(((2 * n) + 1) * 32);
|
||||||
to_hash.reserve_exact(((2 * n) + 5) * 32);
|
|
||||||
for i in 0 .. ROUND.len() {
|
for i in 0 .. ROUND.len() {
|
||||||
to_hash[PREFIX.len() + i] = ROUND.as_bytes()[i] as u8;
|
to_hash[PREFIX.len() + i] = ROUND.as_bytes()[i] as u8;
|
||||||
}
|
}
|
||||||
to_hash.extend(C_out.compress().to_bytes());
|
to_hash.extend(pseudo_out.compress().to_bytes());
|
||||||
to_hash.extend(msg);
|
to_hash.extend(msg);
|
||||||
to_hash.extend(A.compress().to_bytes());
|
|
||||||
to_hash.extend(AH.compress().to_bytes());
|
|
||||||
let mut c = hash_to_scalar(&to_hash);
|
|
||||||
|
|
||||||
|
let mut c;
|
||||||
let mut c1 = Scalar::zero();
|
let mut c1 = Scalar::zero();
|
||||||
let mut i = (r + 1) % n;
|
|
||||||
if i == 0 {
|
let end;
|
||||||
c1 = c;
|
let mut i;
|
||||||
|
|
||||||
|
match A_c1 {
|
||||||
|
Mode::Sign(r, A, AH) => {
|
||||||
|
to_hash.extend(A.compress().to_bytes());
|
||||||
|
to_hash.extend(AH.compress().to_bytes());
|
||||||
|
c = hash_to_scalar(&to_hash);
|
||||||
|
|
||||||
|
end = r;
|
||||||
|
i = (end + 1) % n;
|
||||||
|
if i == 0 {
|
||||||
|
c1 = c;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
#[cfg(feature = "experimental")]
|
||||||
|
Mode::Verify(c1) => {
|
||||||
|
end = 0;
|
||||||
|
i = 0;
|
||||||
|
c = c1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut s = vec![];
|
let mut first = true;
|
||||||
s.resize(n, Scalar::zero());
|
while (i != end) || first {
|
||||||
while i != r {
|
first = false;
|
||||||
s[i] = random_scalar(&mut *rng);
|
|
||||||
let c_p = mu_P * c;
|
let c_p = mu_P * c;
|
||||||
let c_c = mu_C * c;
|
let c_c = mu_C * c;
|
||||||
|
|
||||||
|
@ -171,18 +189,43 @@ pub(crate) fn sign_core<R: RngCore + CryptoRng>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
((D_bytes, c * mu_P, c * mu_C), c1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn sign_core<R: RngCore + CryptoRng>(
|
||||||
|
rng: &mut R,
|
||||||
|
I: &EdwardsPoint,
|
||||||
|
input: &Input,
|
||||||
|
mask: Scalar,
|
||||||
|
msg: &[u8; 32],
|
||||||
|
A: EdwardsPoint,
|
||||||
|
AH: EdwardsPoint
|
||||||
|
) -> (Clsag, EdwardsPoint, Scalar, Scalar) {
|
||||||
|
let r: usize = input.decoys.i.into();
|
||||||
|
|
||||||
|
let pseudo_out = Commitment::new(mask, input.commitment.amount).calculate();
|
||||||
|
let z = input.commitment.mask - mask;
|
||||||
|
|
||||||
|
let H = hash_to_point(&input.decoys.ring[r][0]);
|
||||||
|
let D = H * z;
|
||||||
|
let mut s = Vec::with_capacity(input.decoys.ring.len());
|
||||||
|
for _ in 0 .. input.decoys.ring.len() {
|
||||||
|
s.push(random_scalar(rng));
|
||||||
|
}
|
||||||
|
let ((D_bytes, p, c), c1) = core(&input.decoys.ring, I, &pseudo_out, msg, &D, &s, Mode::Sign(r, A, AH));
|
||||||
|
|
||||||
(
|
(
|
||||||
Clsag {
|
Clsag {
|
||||||
|
D: Key { key: D_bytes },
|
||||||
s: s.iter().map(|s| Key { key: s.to_bytes() }).collect(),
|
s: s.iter().map(|s| Key { key: s.to_bytes() }).collect(),
|
||||||
c1: Key { key: c1.to_bytes() },
|
c1: Key { key: c1.to_bytes() }
|
||||||
D: Key { key: D_bytes }
|
|
||||||
},
|
},
|
||||||
c, mu_C, z, mu_P,
|
pseudo_out,
|
||||||
C_out
|
p,
|
||||||
|
c * z
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
pub fn sign<R: RngCore + CryptoRng>(
|
pub fn sign<R: RngCore + CryptoRng>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
inputs: &[(Scalar, EdwardsPoint, Input)],
|
inputs: &[(Scalar, EdwardsPoint, Input)],
|
||||||
|
@ -209,7 +252,7 @@ pub fn sign<R: RngCore + CryptoRng>(
|
||||||
|
|
||||||
let mut rand_source = [0; 64];
|
let mut rand_source = [0; 64];
|
||||||
rng.fill_bytes(&mut rand_source);
|
rng.fill_bytes(&mut rand_source);
|
||||||
let (mut clsag, c, mu_C, z, mu_P, C_out) = sign_core(
|
let (mut clsag, pseudo_out, p, c) = sign_core(
|
||||||
rng,
|
rng,
|
||||||
&inputs[i].1,
|
&inputs[i].1,
|
||||||
&inputs[i].2,
|
&inputs[i].2,
|
||||||
|
@ -219,29 +262,67 @@ pub fn sign<R: RngCore + CryptoRng>(
|
||||||
nonce * hash_to_point(&inputs[i].2.decoys.ring[usize::from(inputs[i].2.decoys.i)][0])
|
nonce * hash_to_point(&inputs[i].2.decoys.ring[usize::from(inputs[i].2.decoys.i)][0])
|
||||||
);
|
);
|
||||||
clsag.s[inputs[i].2.decoys.i as usize] = Key {
|
clsag.s[inputs[i].2.decoys.i as usize] = Key {
|
||||||
key: (nonce - (c * ((mu_C * z) + (mu_P * inputs[i].0)))).to_bytes()
|
key: (nonce - ((p * inputs[i].0) + c)).to_bytes()
|
||||||
};
|
};
|
||||||
|
|
||||||
res.push((clsag, C_out));
|
res.push((clsag, pseudo_out));
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(res)
|
Some(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Not extensively tested nor guaranteed to have expected parity with Monero
|
||||||
|
#[cfg(feature = "experimental")]
|
||||||
|
pub fn rust_verify(
|
||||||
|
clsag: &Clsag,
|
||||||
|
ring: &[[EdwardsPoint; 2]],
|
||||||
|
I: &EdwardsPoint,
|
||||||
|
pseudo_out: &EdwardsPoint,
|
||||||
|
msg: &[u8; 32]
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let c1 = Scalar::from_canonical_bytes(clsag.c1.key).ok_or(Error::InvalidC1)?;
|
||||||
|
let (_, c1_calculated) = core(
|
||||||
|
ring,
|
||||||
|
I,
|
||||||
|
pseudo_out,
|
||||||
|
msg,
|
||||||
|
&CompressedEdwardsY(clsag.D.key).decompress().ok_or(Error::InvalidD)?.mul_by_cofactor(),
|
||||||
|
&clsag.s.iter().map(|s| Scalar::from_canonical_bytes(s.key).ok_or(Error::InvalidS)).collect::<Result<Vec<_>, _>>()?,
|
||||||
|
Mode::Verify(c1)
|
||||||
|
);
|
||||||
|
if c1_calculated != c1 {
|
||||||
|
Err(Error::InvalidC1)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
// Uses Monero's C verification function to ensure compatibility with Monero
|
// Uses Monero's C verification function to ensure compatibility with Monero
|
||||||
|
#[link(name = "wrapper")]
|
||||||
|
extern "C" {
|
||||||
|
pub(crate) fn c_verify_clsag(
|
||||||
|
serialized_len: usize,
|
||||||
|
serialized: *const u8,
|
||||||
|
ring_size: u8,
|
||||||
|
ring: *const u8,
|
||||||
|
I: *const u8,
|
||||||
|
pseudo_out: *const u8,
|
||||||
|
msg: *const u8
|
||||||
|
) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn verify(
|
pub fn verify(
|
||||||
clsag: &Clsag,
|
clsag: &Clsag,
|
||||||
image: EdwardsPoint,
|
|
||||||
ring: &[[EdwardsPoint; 2]],
|
ring: &[[EdwardsPoint; 2]],
|
||||||
pseudo_out: EdwardsPoint,
|
I: &EdwardsPoint,
|
||||||
|
pseudo_out: &EdwardsPoint,
|
||||||
msg: &[u8; 32]
|
msg: &[u8; 32]
|
||||||
) -> bool {
|
) -> Result<(), Error> {
|
||||||
// Workaround for the fact monero-rs doesn't include the length of clsag.s in clsag encoding
|
// Workaround for the fact monero-rs doesn't include the length of clsag.s in clsag encoding
|
||||||
// despite it being part of clsag encoding. Reason for the patch version pin
|
// despite it being part of clsag encoding. Reason for the patch version pin
|
||||||
let mut serialized = vec![clsag.s.len() as u8];
|
let mut serialized = vec![clsag.s.len() as u8];
|
||||||
clsag.consensus_encode(&mut serialized).unwrap();
|
clsag.consensus_encode(&mut serialized).unwrap();
|
||||||
|
|
||||||
let image_bytes = image.compress().to_bytes();
|
let I_bytes = I.compress().to_bytes();
|
||||||
|
|
||||||
let mut ring_bytes = vec![];
|
let mut ring_bytes = vec![];
|
||||||
for member in ring {
|
for member in ring {
|
||||||
|
@ -252,9 +333,14 @@ pub fn verify(
|
||||||
let pseudo_out_bytes = pseudo_out.compress().to_bytes();
|
let pseudo_out_bytes = pseudo_out.compress().to_bytes();
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
c_verify_clsag(
|
if c_verify_clsag(
|
||||||
serialized.len(), serialized.as_ptr(), image_bytes.as_ptr(),
|
serialized.len(), serialized.as_ptr(),
|
||||||
ring.len() as u8, ring_bytes.as_ptr(), pseudo_out_bytes.as_ptr(), msg.as_ptr()
|
ring.len() as u8, ring_bytes.as_ptr(),
|
||||||
)
|
I_bytes.as_ptr(), pseudo_out_bytes.as_ptr(), msg.as_ptr()
|
||||||
|
) {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Error::InvalidC1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,11 +68,11 @@ impl Details {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct Interim {
|
struct Interim {
|
||||||
|
p: Scalar,
|
||||||
c: Scalar,
|
c: Scalar,
|
||||||
s: Scalar,
|
|
||||||
|
|
||||||
clsag: Clsag,
|
clsag: Clsag,
|
||||||
C_out: EdwardsPoint
|
pseudo_out: EdwardsPoint
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
|
@ -237,7 +237,7 @@ impl Algorithm<Ed25519> for Multisig {
|
||||||
let mut rng = ChaCha12Rng::from_seed(self.transcript.rng_seed(b"decoy_responses", None));
|
let mut rng = ChaCha12Rng::from_seed(self.transcript.rng_seed(b"decoy_responses", None));
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
let (clsag, c, mu_C, z, mu_P, C_out) = sign_core(
|
let (clsag, pseudo_out, p, c) = sign_core(
|
||||||
&mut rng,
|
&mut rng,
|
||||||
&self.image,
|
&self.image,
|
||||||
&self.input(),
|
&self.input(),
|
||||||
|
@ -246,9 +246,9 @@ impl Algorithm<Ed25519> for Multisig {
|
||||||
nonce_sum.0,
|
nonce_sum.0,
|
||||||
self.AH.0.0
|
self.AH.0.0
|
||||||
);
|
);
|
||||||
self.interim = Some(Interim { c: c * mu_P, s: c * mu_C * z, clsag, C_out });
|
self.interim = Some(Interim { p, c, clsag, pseudo_out });
|
||||||
|
|
||||||
let share = dfg::Scalar(nonce.0 - (c * mu_P * view.secret_share().0));
|
let share = dfg::Scalar(nonce.0 - (p * view.secret_share().0));
|
||||||
|
|
||||||
share
|
share
|
||||||
}
|
}
|
||||||
|
@ -262,9 +262,9 @@ impl Algorithm<Ed25519> for Multisig {
|
||||||
let interim = self.interim.as_ref().unwrap();
|
let interim = self.interim.as_ref().unwrap();
|
||||||
|
|
||||||
let mut clsag = interim.clsag.clone();
|
let mut clsag = interim.clsag.clone();
|
||||||
clsag.s[usize::from(self.input().decoys.i)] = Key { key: (sum.0 - interim.s).to_bytes() };
|
clsag.s[usize::from(self.input().decoys.i)] = Key { key: (sum.0 - interim.c).to_bytes() };
|
||||||
if verify(&clsag, self.image, &self.input().decoys.ring, interim.C_out, &self.msg()) {
|
if verify(&clsag, &self.input().decoys.ring, &self.image, &interim.pseudo_out, &self.msg()).is_ok() {
|
||||||
return Some((clsag, interim.C_out));
|
return Some((clsag, interim.pseudo_out));
|
||||||
}
|
}
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -277,7 +277,7 @@ impl Algorithm<Ed25519> for Multisig {
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let interim = self.interim.as_ref().unwrap();
|
let interim = self.interim.as_ref().unwrap();
|
||||||
return (&share.0 * &ED25519_BASEPOINT_TABLE) == (
|
return (&share.0 * &ED25519_BASEPOINT_TABLE) == (
|
||||||
nonce.0 - (interim.c * verification_share.0)
|
nonce.0 - (interim.p * verification_share.0)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,10 +31,6 @@ extern "C" {
|
||||||
serialized_len: usize, serialized: *const u8,
|
serialized_len: usize, serialized: *const u8,
|
||||||
commitments_len: u8, commitments: *const [u8; 32]
|
commitments_len: u8, commitments: *const [u8; 32]
|
||||||
) -> bool;
|
) -> bool;
|
||||||
pub(crate) fn c_verify_clsag(
|
|
||||||
serialized_len: usize, serialized: *const u8, I: *const u8,
|
|
||||||
ring_size: u8, ring: *const u8, msg: *const u8, pseudo_out: *const u8
|
|
||||||
) -> bool;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
|
|
|
@ -21,7 +21,7 @@ const RING_LEN: u64 = 11;
|
||||||
const AMOUNT: u64 = 1337;
|
const AMOUNT: u64 = 1337;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_single() {
|
fn clsag() {
|
||||||
let msg = [1; 32];
|
let msg = [1; 32];
|
||||||
|
|
||||||
let mut secrets = [Scalar::zero(), Scalar::zero()];
|
let mut secrets = [Scalar::zero(), Scalar::zero()];
|
||||||
|
@ -57,12 +57,14 @@ fn test_single() {
|
||||||
random_scalar(&mut OsRng),
|
random_scalar(&mut OsRng),
|
||||||
msg
|
msg
|
||||||
).unwrap().swap_remove(0);
|
).unwrap().swap_remove(0);
|
||||||
assert!(clsag::verify(&clsag, image, &ring, pseudo_out, &msg));
|
clsag::verify(&clsag, &ring, &image, &pseudo_out, &msg).unwrap();
|
||||||
|
#[cfg(feature = "experimental")]
|
||||||
|
clsag::rust_verify(&clsag, &ring, &image, &pseudo_out, &msg).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "multisig")]
|
#[cfg(feature = "multisig")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_multisig() -> Result<(), MultisigError> {
|
fn clsag_multisig() -> Result<(), MultisigError> {
|
||||||
let (keys, group_private) = generate_keys();
|
let (keys, group_private) = generate_keys();
|
||||||
let t = keys[0].params().t();
|
let t = keys[0].params().t();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue