Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RingMLSAG derives Message #23

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 4 additions & 29 deletions consensus/api/src/conversions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use transaction::{
encrypted_fog_hint::EncryptedFogHint,
range::Range,
ring_signature::{
Blinding, CurvePoint, CurveScalar, Error as RingSigError, KeyImage, RingMLSAG, Scalar,
Blinding, CurvePoint, CurveScalar, Error as RingSigError, KeyImage, RingMLSAG,
SignatureRctBulletproofs,
},
tx,
Expand Down Expand Up @@ -153,31 +153,6 @@ impl TryFrom<&external::CurvePoint> for CurvePoint {
}
}

/// Convert Scalar --> external::CurveScalar.
impl From<&Scalar> for external::CurveScalar {
fn from(other: &Scalar) -> Self {
let mut curve_scalar = external::CurveScalar::new();
curve_scalar.set_data(other.as_bytes().to_vec());
curve_scalar
}
}

/// Convert external::CurveScalar --> Scalar.
impl TryFrom<&external::CurveScalar> for Scalar {
type Error = ConversionError;

fn try_from(source: &external::CurveScalar) -> Result<Self, Self::Error> {
let bytes: &[u8] = source.get_data();

let mut arr = [0u8; 32];
if bytes.len() != arr.len() {
return Err(ConversionError::ArrayCastError);
}
arr.copy_from_slice(bytes);
Ok(Scalar::from_bytes_mod_order(arr))
}
}

impl From<&CompressedRistretto> for external::CompressedRistretto {
fn from(source: &CompressedRistretto) -> Self {
let mut compressed_ristretto = external::CompressedRistretto::new();
Expand Down Expand Up @@ -527,10 +502,10 @@ impl TryFrom<&external::RingMLSAG> for RingMLSAG {
type Error = ConversionError;

fn try_from(source: &external::RingMLSAG) -> Result<Self, Self::Error> {
let c_zero = Scalar::try_from(source.get_c_zero())?;
let mut responses: Vec<Scalar> = Vec::new();
let c_zero = CurveScalar::try_from(source.get_c_zero())?;
let mut responses: Vec<CurveScalar> = Vec::new();
for response in source.get_responses() {
responses.push(Scalar::try_from(response)?);
responses.push(CurveScalar::try_from(response)?);
}
let key_image = KeyImage::try_from(source.get_key_image())?;

Expand Down
65 changes: 40 additions & 25 deletions transaction/core/src/ring_signature/curve_scalar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,75 +17,88 @@ use rand_core::{CryptoRng, RngCore};
use serde::{Deserialize, Serialize};

#[derive(Copy, Clone, Default, Eq, Serialize, Deserialize, Digestible)]
pub struct CurveScalar(pub(crate) Scalar);
pub struct CurveScalar {
pub scalar: Scalar,
}

impl CurveScalar {
/// Construct a `CurveScalar` by reducing a 256-bit little-endian integer
/// modulo the group order \\( \ell \\).
pub fn from_bytes_mod_order(bytes: [u8; 32]) -> Self {
Self(Scalar::from_bytes_mod_order(bytes))
Self {
scalar: Scalar::from_bytes_mod_order(bytes),
}
}

/// The little-endian byte encoding of the integer representing this Scalar.
pub fn as_bytes(&self) -> &[u8; 32] {
self.0.as_bytes()
self.scalar.as_bytes()
}
}

impl keys::FromRandom for CurveScalar {
fn from_random(csprng: &mut (impl CryptoRng + RngCore)) -> Self {
Self(Scalar::random(csprng))
Self {
scalar: Scalar::random(csprng),
}
}
}

impl AsRef<[u8; 32]> for CurveScalar {
impl From<Scalar> for CurveScalar {
#[inline]
fn as_ref(&self) -> &[u8; 32] {
self.0.as_bytes()
fn from(scalar: Scalar) -> Self {
Self { scalar }
}
}

impl AsRef<[u8]> for CurveScalar {
impl From<u64> for CurveScalar {
#[inline]
fn as_ref(&self) -> &[u8] {
self.0.as_bytes()
fn from(val: u64) -> Self {
Self {
scalar: Scalar::from(val),
}
}
}

impl AsRef<Scalar> for CurveScalar {
impl AsRef<[u8; 32]> for CurveScalar {
#[inline]
fn as_ref(&self) -> &Scalar {
&self.0
fn as_ref(&self) -> &[u8; 32] {
self.scalar.as_bytes()
}
}

impl From<Scalar> for CurveScalar {
// Implements Ord, PartialOrd, PartialEq, Hash. Requires AsRef<[u8;32]>.
deduce_core_traits_from_public_bytes! { CurveScalar }

impl AsRef<[u8]> for CurveScalar {
#[inline]
fn from(scalar: Scalar) -> Self {
Self(scalar)
fn as_ref(&self) -> &[u8] {
self.scalar.as_bytes()
}
}

impl From<u64> for CurveScalar {
impl AsRef<Scalar> for CurveScalar {
#[inline]
fn from(val: u64) -> Self {
Self(Scalar::from(val))
fn as_ref(&self) -> &Scalar {
&self.scalar
}
}

impl Into<Scalar> for CurveScalar {
fn into(self) -> Scalar {
self.0
self.scalar
}
}

impl ReprBytes32 for CurveScalar {
type Error = Error;
fn to_bytes(&self) -> [u8; 32] {
self.0.to_bytes()
self.scalar.to_bytes()
}
fn from_bytes(src: &[u8; 32]) -> Result<Self, Error> {
Ok(Self(Scalar::from_bytes_mod_order(*src)))
Ok(Self {
scalar: Scalar::from_bytes_mod_order(*src),
})
}
}

Expand All @@ -95,9 +108,11 @@ impl fmt::Debug for CurveScalar {
}
}

// Implements prost::Message. Requires Debug and ReprBytes32.
prost_message_helper32! { CurveScalar }

// Implements try_from<&[u8;32]> and try_from<&[u8]>. Requires ReprBytes32.
try_from_helper32! { CurveScalar }
deduce_core_traits_from_public_bytes! { CurveScalar }

#[cfg(test)]
mod tests {
Expand All @@ -107,7 +122,7 @@ mod tests {
fn test_to_from_bytes() {
let one = Scalar::one();
let curve_scalar = CurveScalar::from_bytes_mod_order(*one.as_bytes());
assert_eq!(curve_scalar.0, one);
assert_eq!(curve_scalar.scalar, one);
assert_eq!(curve_scalar.as_bytes(), one.as_bytes());
}

Expand All @@ -123,7 +138,7 @@ mod tests {
let curve_scalar = CurveScalar::from_bytes_mod_order(l_plus_two_bytes);
let two: Scalar = Scalar::one() + Scalar::one();

assert_eq!(curve_scalar.0, two);
assert_eq!(curve_scalar.scalar, two);
}

#[test]
Expand Down
103 changes: 21 additions & 82 deletions transaction/core/src/ring_signature/mlsag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use crate::{
onetime_keys::compute_key_image,
ring_signature::{
encoding::{read_u8_32, write_u8_32},
Blinding, Error, KeyImage, Scalar, GENERATORS,
Blinding, CurveScalar, Error, KeyImage, Scalar, GENERATORS,
},
};

Expand All @@ -36,88 +36,21 @@ fn hash_to_point(ristretto_public: &RistrettoPublic) -> RistrettoPoint {

/// MLSAG for a ring of public keys and amount commitments.
/// Note: Serialize and Deserialize appear to be cruft left over from sdk_json_interface.
#[derive(Clone, Debug, Default, Digestible, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Clone, Digestible, PartialEq, Eq, Serialize, Deserialize, Message)]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes to this struct are the main point of this PR.

pub struct RingMLSAG {
/// The initial challenge `c[0]`.
pub c_zero: Scalar,
#[prost(message, required, tag = "1")]
pub c_zero: CurveScalar,

/// Responses `r_{0,0}, r_{0,1}, ... , r_{ring_size-1,0}, r_{ring_size-1,1}`.
pub responses: Vec<Scalar>,
#[prost(message, repeated, tag = "2")]
pub responses: Vec<CurveScalar>,

/// Key image "spent" by this signature.
#[prost(message, required, tag = "3")]
pub key_image: KeyImage,
}

const C_ZERO_TAG: u32 = 1;
const RESPONSES_TAG: u32 = 2;
const KEY_IMAGE_TAG: u32 = 3;

/// Message allows RingMLSAG to be serialized with Prost (without using a protobuf).
impl Message for RingMLSAG {
/// Encodes the message to the buffer.
fn encode_raw<B>(&self, buf: &mut B)
where
B: BufMut,
Self: Sized,
{
// c_zero
write_u8_32(self.c_zero.to_bytes(), C_ZERO_TAG, buf);

// responses
for response in &self.responses {
write_u8_32(response.to_bytes(), RESPONSES_TAG, buf);
}

// key_image
write_u8_32(self.key_image.to_bytes(), KEY_IMAGE_TAG, buf);
}

/// Decodes a field from a buffer, and merges it into `self`.
fn merge_field<B>(
&mut self,
tag: u32,
wire_type: WireType,
buf: &mut B,
ctx: DecodeContext,
) -> Result<(), DecodeError>
where
B: Buf,
Self: Sized,
{
match tag {
C_ZERO_TAG => {
self.c_zero = Scalar::from_bytes_mod_order(read_u8_32(wire_type, buf)?);
Ok(())
}
RESPONSES_TAG => {
let response = Scalar::from_bytes_mod_order(read_u8_32(wire_type, buf)?);
self.responses.push(response);
Ok(())
}
KEY_IMAGE_TAG => {
self.key_image = KeyImage::from(read_u8_32(wire_type, buf)?);
Ok(())
}
_ => skip_field(wire_type, tag, buf, ctx),
}
}

/// Returns the encoded length of the message without a length delimiter.
fn encoded_len(&self) -> usize {
let c_zero_len = key_len(C_ZERO_TAG) + encoded_len_varint(32 as u64) + 32;
let responses_len =
(key_len(RESPONSES_TAG) + encoded_len_varint(32 as u64) + 32) * self.responses.len();
let key_image_len = key_len(KEY_IMAGE_TAG) + encoded_len_varint(32 as u64) + 32;

c_zero_len + responses_len + key_image_len
}

/// Clears the message, resetting all fields to their default.
fn clear(&mut self) {
*self = Self::default();
}
}

impl RingMLSAG {
// Sign a ring of input addresses and amount commitments.
//
Expand Down Expand Up @@ -279,9 +212,11 @@ impl RingMLSAG {
}
}

let responses: Vec<CurveScalar> = r.into_iter().map(CurveScalar::from).collect();

Ok(RingMLSAG {
c_zero: c[0],
responses: r,
c_zero: CurveScalar::from(c[0]),
responses,
key_image,
})
}
Expand Down Expand Up @@ -314,7 +249,11 @@ impl RingMLSAG {
.try_into()
.map_err(|_e| Error::InvalidKeyImage)?;

let r = &self.responses;
let r: Vec<Scalar> = self
.responses
.iter()
.map(|response| response.scalar)
.collect();

// Output commitment must decompress.
let output_commitment: Commitment = Commitment::try_from(output_commitment)?;
Expand All @@ -329,7 +268,7 @@ impl RingMLSAG {
for (i, (P_i, input_commitment)) in decompressed_ring.iter().enumerate() {
let c_i = if i == 0 {
// Initialize loop using the signature's c_0 term.
self.c_zero
self.c_zero.scalar
} else {
recomputed_c[i]
};
Expand All @@ -356,7 +295,7 @@ impl RingMLSAG {
};
}

if self.c_zero == recomputed_c[0] {
if self.c_zero.scalar == recomputed_c[0] {
Ok(())
} else {
Err(Error::InvalidSignature)
Expand Down Expand Up @@ -390,7 +329,7 @@ mod mlsag_tests {
use crate::{
onetime_keys::compute_key_image,
proptest_fixtures::*,
ring_signature::{mlsag::RingMLSAG, Error, Scalar, GENERATORS},
ring_signature::{mlsag::RingMLSAG, CurveScalar, Error, Scalar, GENERATORS},
CompressedCommitment,
};

Expand Down Expand Up @@ -484,7 +423,7 @@ mod mlsag_tests {

// All responses should be non-zero.
for r in &signature.responses {
assert_ne!(*r, Scalar::zero());
assert_ne!(r.scalar, Scalar::zero());
}
}

Expand Down Expand Up @@ -897,7 +836,7 @@ mod mlsag_tests {
// Modify the signature to have too many responses.
{
let mut invalid_signature = signature.clone();
invalid_signature.responses.push(Scalar::random(&mut rng));
invalid_signature.responses.push(CurveScalar::from_random(&mut rng));

let result =
invalid_signature.verify(&params.message, &params.ring, &output_commitment);
Expand Down