Work in progress: structure preserving signature scheme
This commit is contained in:
Generated
+16
@@ -2950,6 +2950,22 @@ dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-divisible-ecash"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bls12_381",
|
||||
"bs58",
|
||||
"criterion",
|
||||
"digest 0.9.0",
|
||||
"ff 0.10.1",
|
||||
"group 0.10.0",
|
||||
"itertools",
|
||||
"rand 0.8.5",
|
||||
"sha2",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-gateway"
|
||||
version = "0.12.1"
|
||||
|
||||
@@ -11,11 +11,11 @@ use crate::error::{CompactEcashError, Result};
|
||||
use crate::scheme::aggregation::aggregate_verification_keys;
|
||||
use crate::scheme::setup::Parameters;
|
||||
use crate::scheme::SignerIndex;
|
||||
use crate::utils::Polynomial;
|
||||
use crate::utils::{
|
||||
try_deserialize_g1_projective, try_deserialize_g2_projective, try_deserialize_scalar,
|
||||
try_deserialize_scalar_vec,
|
||||
};
|
||||
use crate::utils::Polynomial;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct SecretKeyAuth {
|
||||
@@ -242,13 +242,13 @@ impl<'a> Mul<Scalar> for &'a VerificationKeyAuth {
|
||||
}
|
||||
|
||||
impl<T> Sum<T> for VerificationKeyAuth
|
||||
where
|
||||
T: Borrow<VerificationKeyAuth>,
|
||||
where
|
||||
T: Borrow<VerificationKeyAuth>,
|
||||
{
|
||||
#[inline]
|
||||
fn sum<I>(iter: I) -> Self
|
||||
where
|
||||
I: Iterator<Item = T>,
|
||||
where
|
||||
I: Iterator<Item=T>,
|
||||
{
|
||||
let mut peekable = iter.peekable();
|
||||
let head_attributes = match peekable.peek() {
|
||||
@@ -440,3 +440,4 @@ pub fn ttp_keygen(
|
||||
|
||||
Ok(keypairs)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
use thiserror::Error;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, DivisibleEcashError>;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum DivisibleEcashError {
|
||||
#[error("Setup error: {0}")]
|
||||
Setup(String),
|
||||
|
||||
#[error("Aggregation error: {0}")]
|
||||
Aggregation(String),
|
||||
|
||||
#[error("Withdrawal Request Verification related error: {0}")]
|
||||
WithdrawalRequestVerification(String),
|
||||
|
||||
#[error("Deserialization error: {0}")]
|
||||
Deserialization(String),
|
||||
|
||||
#[error("Interpolation error: {0}")]
|
||||
Interpolation(String),
|
||||
|
||||
#[error("Issuance Verification related error: {0}")]
|
||||
IssuanceVfy(String),
|
||||
|
||||
#[error("Spend Verification related error: {0}")]
|
||||
Spend(String),
|
||||
|
||||
#[error("Tried to deserialize {object} with bytes of invalid length. Expected {actual} < {} or {modulus_target} % {modulus} == 0")]
|
||||
DeserializationInvalidLength {
|
||||
actual: usize,
|
||||
target: usize,
|
||||
modulus_target: usize,
|
||||
modulus: usize,
|
||||
object: String,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
use bls12_381::Scalar;
|
||||
|
||||
mod proofs;
|
||||
mod scheme;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
mod error;
|
||||
mod error;
|
||||
mod utils;
|
||||
mod traits;
|
||||
|
||||
pub type Attribute = Scalar;
|
||||
@@ -2,4 +2,5 @@ pub mod aggregation;
|
||||
pub mod identify;
|
||||
pub mod keygen;
|
||||
pub mod setup;
|
||||
pub mod withdrawal;
|
||||
pub mod withdrawal;
|
||||
pub mod structure_preserving_signature;
|
||||
@@ -0,0 +1,140 @@
|
||||
use bls12_381::{G1Affine, G1Projective, G2Affine, G2Prepared, G2Projective, Scalar};
|
||||
use ff::Field;
|
||||
use rand::thread_rng;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::utils::hash_g1;
|
||||
|
||||
const MAX_COIN_VALUE: u64 = 32;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GroupParameters {
|
||||
/// Generator of the G1 group
|
||||
g1: G1Affine,
|
||||
/// Generator of the G2 group
|
||||
g2: G2Affine,
|
||||
/// Precomputed G2 generator used for the miller loop
|
||||
_g2_prepared_miller: G2Prepared,
|
||||
/// Value of wallet
|
||||
L: u64,
|
||||
}
|
||||
|
||||
impl GroupParameters {
|
||||
pub fn new() -> Result<GroupParameters> {
|
||||
Ok(GroupParameters {
|
||||
g1: G1Affine::generator(),
|
||||
g2: G2Affine::generator(),
|
||||
_g2_prepared_miller: G2Prepared::from(G2Affine::generator()),
|
||||
L: MAX_COIN_VALUE,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn gen1(&self) -> &G1Affine {
|
||||
&self.g1
|
||||
}
|
||||
|
||||
pub(crate) fn gen2(&self) -> &G2Affine {
|
||||
&self.g2
|
||||
}
|
||||
|
||||
pub(crate) fn prepared_miller_g2(&self) -> &G2Prepared {
|
||||
&self._g2_prepared_miller
|
||||
}
|
||||
|
||||
pub(crate) fn getL(&self) -> u64 { self.L }
|
||||
|
||||
pub(crate) fn random_scalar(&self) -> Scalar {
|
||||
// lazily-initialized thread-local random number generator, seeded by the system
|
||||
let mut rng = thread_rng();
|
||||
Scalar::random(&mut rng)
|
||||
}
|
||||
|
||||
pub(crate) fn n_random_scalars(&self, n: usize) -> Vec<Scalar> {
|
||||
(0..n).map(|_| self.random_scalar()).collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Parameters {
|
||||
paramsUser: ParametersUser,
|
||||
paramsAuth: ParametersAuthority,
|
||||
}
|
||||
|
||||
|
||||
impl Parameters {
|
||||
pub fn new(grp: GroupParameters) -> Parameters {
|
||||
let g1 = grp.gen1();
|
||||
let g2 = grp.gen2();
|
||||
let gamma1 = hash_g1("gamma1");
|
||||
let gamma2 = hash_g1("gamma2");
|
||||
let eta = hash_g1("eta");
|
||||
let omega = hash_g1("omega");
|
||||
|
||||
let z = grp.random_scalar();
|
||||
let y = grp.random_scalar();
|
||||
|
||||
let vec_a = grp.n_random_scalars(grp.getL() as usize);
|
||||
|
||||
let sigma = g1 * z;
|
||||
let theta = eta * z;
|
||||
|
||||
let sigmasUser: Vec<G1Projective> = (1..=grp.getL()).map(|i| sigma * (y * Scalar::from(i))).collect();
|
||||
let thetasUser: Vec<G1Projective> = (1..=grp.getL()).map(|i| theta * (y * Scalar::from(i))).collect();
|
||||
|
||||
let deltasAuth: Vec<G2Projective> = (0..=grp.getL() - 1).map(|i| g2 * (y * Scalar::from(i))).collect();
|
||||
let etasUser: Vec<G1Projective> = vec_a.iter().map(|x| g1 * x).collect();
|
||||
|
||||
let mut etasAuth: Vec<G2Projective> = Default::default();
|
||||
for l in 1..=grp.getL() {
|
||||
for k in 0..=l - 1 {
|
||||
etasAuth.push(g2 * (vec_a[l as usize].neg() * (y * Scalar::from(k))));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Run the key generation algorithm of the Structure-Preserving Signature scheme
|
||||
|
||||
// Compute signature for each pair sigma, theta
|
||||
let paramsUser = ParametersUser {
|
||||
g1: *g1,
|
||||
g2: *g2,
|
||||
gamma1,
|
||||
gamma2,
|
||||
eta,
|
||||
omega,
|
||||
etas: etasUser,
|
||||
sigmas: sigmasUser,
|
||||
thetas: thetasUser,
|
||||
};
|
||||
|
||||
let paramsAuth = ParametersAuthority {
|
||||
deltas: deltasAuth,
|
||||
etas: etasAuth,
|
||||
};
|
||||
|
||||
return Parameters {
|
||||
paramsUser,
|
||||
paramsAuth,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ParametersUser {
|
||||
/// Generator of the G1 group
|
||||
g1: G1Affine,
|
||||
/// Generator of the G2 group
|
||||
g2: G2Affine,
|
||||
gamma1: G1Projective,
|
||||
gamma2: G1Projective,
|
||||
eta: G1Projective,
|
||||
omega: G1Projective,
|
||||
etas: Vec<G1Projective>,
|
||||
sigmas: Vec<G1Projective>,
|
||||
thetas: Vec<G1Projective>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ParametersAuthority {
|
||||
deltas: Vec<G2Projective>,
|
||||
etas: Vec<G2Projective>,
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use bls12_381::{G1Projective, G2Projective, Scalar};
|
||||
use group::Curve;
|
||||
|
||||
use crate::Attribute;
|
||||
use crate::scheme::setup::GroupParameters;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct SPSVerificationKey {
|
||||
pub grparams: GroupParameters,
|
||||
pub Us: Vec<G1Projective>,
|
||||
pub Ws: Vec<G2Projective>,
|
||||
pub Y: G2Projective,
|
||||
pub Z: G2Projective,
|
||||
}
|
||||
|
||||
pub(crate) struct SPSSecretKey {
|
||||
spsVK: SPSVerificationKey,
|
||||
us: Vec<Scalar>,
|
||||
ws: Vec<Scalar>,
|
||||
y: Scalar,
|
||||
z: Scalar,
|
||||
}
|
||||
|
||||
impl SPSSecretKey {
|
||||
pub fn z(&self) -> Scalar { self.z }
|
||||
pub fn y(&self) -> Scalar { self.y }
|
||||
pub fn sign(&self, grparams: GroupParameters, attributes: Vec<Attribute>) -> SPSSignature {
|
||||
let r = grparams.random_scalar();
|
||||
let R = grparams.gen1() * r;
|
||||
let prod: Vec<Scalar> = attributes.iter().zip(self.ws.iter()).map(|(w_i, m_i)| m_i * w_i.neg()).collect();
|
||||
let Z = grparams.gen1() * (self.z() - r * self.y()) + prod.iter().fold(1 | acc, x | acc * x);
|
||||
// let sum = a.iter().fold(0, |acc, x| acc + x);
|
||||
// let Z: G1Projective = grparams.gen1() * (self.z() - r * self.y())
|
||||
// + attributes
|
||||
// .iter()
|
||||
// .zip(self.ws.iter())
|
||||
// .map(|(w_i, m_i)| m_i * w_i.neg()).product();
|
||||
SPSSignature {}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SPSKeyPair {
|
||||
spsSK: SPSSecretKey,
|
||||
spsVK: SPSVerificationKey,
|
||||
}
|
||||
|
||||
impl SPSKeyPair {
|
||||
pub fn new(grparams: GroupParameters, a: usize, b: usize) -> SPSKeyPair {
|
||||
let us = grparams.n_random_scalars(b);
|
||||
let ws = grparams.n_random_scalars(a);
|
||||
let y = grparams.random_scalar();
|
||||
let z = grparams.random_scalar();
|
||||
let Us: Vec<G1Projective> = us.iter().map(|u| grparams.gen1() * u).collect();
|
||||
let Y = grparams.gen2() * y;
|
||||
let Ws: Vec<G2Projective> = ws.iter().map(|w| grparams.gen2() * w).collect();
|
||||
let Z = grparams.gen2() * z;
|
||||
|
||||
let spsVK = SPSVerificationKey {
|
||||
grparams,
|
||||
Us,
|
||||
Ws,
|
||||
Y,
|
||||
Z,
|
||||
};
|
||||
let spsSK = SPSSecretKey {
|
||||
spsVK: spsVK.clone(),
|
||||
us,
|
||||
ws,
|
||||
y,
|
||||
z,
|
||||
};
|
||||
SPSKeyPair { spsSK, spsVK }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SPSSignature {}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
use crate::error::DivisibleEcashError;
|
||||
|
||||
pub trait Bytable
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
fn to_byte_vec(&self) -> Vec<u8>;
|
||||
|
||||
fn try_from_byte_slice(slice: &[u8]) -> Result<Self, DivisibleEcashError>;
|
||||
}
|
||||
|
||||
pub trait Base58
|
||||
where
|
||||
Self: Bytable,
|
||||
{
|
||||
fn try_from_bs58<S: AsRef<str>>(x: S) -> Result<Self, DivisibleEcashError> {
|
||||
Self::try_from_byte_slice(&bs58::decode(x.as_ref()).into_vec().unwrap())
|
||||
}
|
||||
fn to_bs58(&self) -> String {
|
||||
bs58::encode(self.to_byte_vec()).into_string()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,432 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use core::iter::Sum;
|
||||
use core::ops::Mul;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::ops::Neg;
|
||||
|
||||
use bls12_381::{
|
||||
G1Affine, G1Projective, G2Affine, G2Prepared, G2Projective, multi_miller_loop, Scalar,
|
||||
};
|
||||
use bls12_381::hash_to_curve::{ExpandMsgXmd, HashToCurve, HashToField};
|
||||
use ff::Field;
|
||||
use group::{Curve, Group};
|
||||
|
||||
use crate::error::{DivisibleEcashError, Result};
|
||||
use crate::scheme::setup::GroupParameters;
|
||||
use crate::traits::Bytable;
|
||||
|
||||
pub struct Polynomial {
|
||||
coefficients: Vec<Scalar>,
|
||||
}
|
||||
|
||||
impl Polynomial {
|
||||
// for polynomial of degree n, we generate n+1 values
|
||||
// (for example for degree 1, like y = x + 2, we need [2,1])
|
||||
pub fn new_random(grp: &GroupParameters, degree: u64) -> Self {
|
||||
Polynomial {
|
||||
coefficients: grp.n_random_scalars((degree + 1) as usize),
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluates the polynomial at point x.
|
||||
pub fn evaluate(&self, x: &Scalar) -> Scalar {
|
||||
if self.coefficients.is_empty() {
|
||||
Scalar::zero()
|
||||
// if x is zero then we can ignore most of the expensive computation and
|
||||
// just return the last term of the polynomial
|
||||
} else if x.is_zero() {
|
||||
// we checked that coefficients are not empty so unwrap here is fine
|
||||
*self.coefficients.first().unwrap()
|
||||
} else {
|
||||
self.coefficients
|
||||
.iter()
|
||||
.enumerate()
|
||||
// coefficient[n] * x ^ n
|
||||
.map(|(i, coefficient)| coefficient * x.pow(&[i as u64, 0, 0, 0]))
|
||||
.sum()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn generate_lagrangian_coefficients_at_origin(points: &[u64]) -> Vec<Scalar> {
|
||||
let x = Scalar::zero();
|
||||
|
||||
points
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, point_i)| {
|
||||
let mut numerator = Scalar::one();
|
||||
let mut denominator = Scalar::one();
|
||||
let xi = Scalar::from(*point_i);
|
||||
|
||||
for (j, point_j) in points.iter().enumerate() {
|
||||
if j != i {
|
||||
let xj = Scalar::from(*point_j);
|
||||
|
||||
// numerator = (x - xs[0]) * ... * (x - xs[j]), j != i
|
||||
numerator *= x - xj;
|
||||
|
||||
// denominator = (xs[i] - x[0]) * ... * (xs[i] - x[j]), j != i
|
||||
denominator *= xi - xj;
|
||||
}
|
||||
}
|
||||
// numerator / denominator
|
||||
numerator * denominator.invert().unwrap()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Performs a Lagrange interpolation at the origin for a polynomial defined by `points` and `values`.
|
||||
/// It can be used for Scalars, G1 and G2 points.
|
||||
pub(crate) fn perform_lagrangian_interpolation_at_origin<T>(
|
||||
points: &[SignerIndex],
|
||||
values: &[T],
|
||||
) -> Result<T>
|
||||
where
|
||||
T: Sum,
|
||||
for<'a> &'a T: Mul<Scalar, Output=T>,
|
||||
{
|
||||
if points.is_empty() || values.is_empty() {
|
||||
return Err(DivisibleEcashError::Interpolation(
|
||||
"Tried to perform lagrangian interpolation for an empty set of coordinates".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
if points.len() != values.len() {
|
||||
return Err(DivisibleEcashError::Interpolation(
|
||||
"Tried to perform lagrangian interpolation for an incomplete set of coordinates"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let coefficients = generate_lagrangian_coefficients_at_origin(points);
|
||||
|
||||
Ok(coefficients
|
||||
.into_iter()
|
||||
.zip(values.iter())
|
||||
.map(|(coeff, val)| val * coeff)
|
||||
.sum())
|
||||
}
|
||||
|
||||
// A temporary way of hashing particular message into G1.
|
||||
// Implementation idea was taken from `threshold_crypto`:
|
||||
// https://github.com/poanetwork/threshold_crypto/blob/7709462f2df487ada3bb3243060504b5881f2628/src/lib.rs#L691
|
||||
// Eventually it should get replaced by, most likely, the osswu map
|
||||
// method once ideally it's implemented inside the pairing crate.
|
||||
|
||||
// note: I have absolutely no idea what are the correct domains for those. I just used whatever
|
||||
// was given in the test vectors of `Hashing to Elliptic Curves draft-irtf-cfrg-hash-to-curve-11`
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-J.9.1
|
||||
const G1_HASH_DOMAIN: &[u8] = b"QUUX-V01-CS02-with-BLS12381G1_XMD:SHA-256_SSWU_RO_";
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-K.1
|
||||
const SCALAR_HASH_DOMAIN: &[u8] = b"QUUX-V01-CS02-with-expander";
|
||||
|
||||
pub fn hash_g1<M: AsRef<[u8]>>(msg: M) -> G1Projective {
|
||||
<G1Projective as HashToCurve<ExpandMsgXmd<sha2::Sha256>>>::hash_to_curve(msg, G1_HASH_DOMAIN)
|
||||
}
|
||||
|
||||
pub fn hash_to_scalar<M: AsRef<[u8]>>(msg: M) -> Scalar {
|
||||
let mut output = vec![Scalar::zero()];
|
||||
|
||||
Scalar::hash_to_field::<ExpandMsgXmd<sha2::Sha256>>(
|
||||
msg.as_ref(),
|
||||
SCALAR_HASH_DOMAIN,
|
||||
&mut output,
|
||||
);
|
||||
output[0]
|
||||
}
|
||||
|
||||
pub fn try_deserialize_scalar_vec(
|
||||
expected_len: u64,
|
||||
bytes: &[u8],
|
||||
err: DivisibleEcashError,
|
||||
) -> Result<Vec<Scalar>> {
|
||||
if bytes.len() != expected_len as usize * 32 {
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
let mut out = Vec::with_capacity(expected_len as usize);
|
||||
for i in 0..expected_len as usize {
|
||||
let s_bytes = bytes[i * 32..(i + 1) * 32].try_into().unwrap();
|
||||
let s = match Into::<Option<Scalar>>::into(Scalar::from_bytes(&s_bytes)) {
|
||||
None => return Err(err),
|
||||
Some(scalar) => scalar,
|
||||
};
|
||||
out.push(s)
|
||||
}
|
||||
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
pub fn try_deserialize_scalar(bytes: &[u8; 32], err: DivisibleEcashError) -> Result<Scalar> {
|
||||
Into::<Option<Scalar>>::into(Scalar::from_bytes(bytes)).ok_or(err)
|
||||
}
|
||||
|
||||
pub fn try_deserialize_g1_projective(
|
||||
bytes: &[u8; 48],
|
||||
err: DivisibleEcashError,
|
||||
) -> Result<G1Projective> {
|
||||
Into::<Option<G1Affine>>::into(G1Affine::from_compressed(bytes))
|
||||
.ok_or(err)
|
||||
.map(G1Projective::from)
|
||||
}
|
||||
|
||||
pub fn try_deserialize_g2_projective(
|
||||
bytes: &[u8; 96],
|
||||
err: DivisibleEcashError,
|
||||
) -> Result<G2Projective> {
|
||||
Into::<Option<G2Affine>>::into(G2Affine::from_compressed(bytes))
|
||||
.ok_or(err)
|
||||
.map(G2Projective::from)
|
||||
}
|
||||
|
||||
/// Checks whether e(P, Q) * e(-R, S) == id
|
||||
pub fn check_bilinear_pairing(p: &G1Affine, q: &G2Prepared, r: &G1Affine, s: &G2Prepared) -> bool {
|
||||
// checking e(P, Q) * e(-R, S) == id
|
||||
// is equivalent to checking e(P, Q) == e(R, S)
|
||||
// but requires only a single final exponentiation rather than two of them
|
||||
// and therefore, as seen via benchmarks.rs, is almost 50% faster
|
||||
// (1.47ms vs 2.45ms, tested on R9 5900X)
|
||||
|
||||
let multi_miller = multi_miller_loop(&[(p, q), (&r.neg(), s)]);
|
||||
multi_miller.final_exponentiation().is_identity().into()
|
||||
}
|
||||
|
||||
pub type SignerIndex = u64;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct Signature(pub(crate) G1Projective, pub(crate) G1Projective);
|
||||
|
||||
pub type PartialSignature = Signature;
|
||||
|
||||
impl TryFrom<&[u8]> for Signature {
|
||||
type Error = DivisibleEcashError;
|
||||
|
||||
fn try_from(bytes: &[u8]) -> Result<Signature> {
|
||||
if bytes.len() != 96 {
|
||||
return Err(DivisibleEcashError::Deserialization(format!(
|
||||
"Signature must be exactly 96 bytes, got {}",
|
||||
bytes.len()
|
||||
)));
|
||||
}
|
||||
|
||||
let sig1_bytes: &[u8; 48] = &bytes[..48].try_into().expect("Slice size != 48");
|
||||
let sig2_bytes: &[u8; 48] = &bytes[48..].try_into().expect("Slice size != 48");
|
||||
|
||||
let sig1 = try_deserialize_g1_projective(
|
||||
sig1_bytes,
|
||||
DivisibleEcashError::Deserialization("Failed to deserialize compressed sig1".to_string()),
|
||||
)?;
|
||||
|
||||
let sig2 = try_deserialize_g1_projective(
|
||||
sig2_bytes,
|
||||
DivisibleEcashError::Deserialization("Failed to deserialize compressed sig2".to_string()),
|
||||
)?;
|
||||
|
||||
Ok(Signature(sig1, sig2))
|
||||
}
|
||||
}
|
||||
|
||||
impl Signature {
|
||||
pub(crate) fn sig1(&self) -> &G1Projective {
|
||||
&self.0
|
||||
}
|
||||
|
||||
pub(crate) fn sig2(&self) -> &G1Projective {
|
||||
&self.1
|
||||
}
|
||||
|
||||
pub fn randomise(&self, grp: &GroupParameters) -> (Signature, Scalar) {
|
||||
let r = grp.random_scalar();
|
||||
let r_prime = grp.random_scalar();
|
||||
let h_prime = self.0 * r_prime;
|
||||
let s_prime = (self.1 * r_prime) + (h_prime * r);
|
||||
(Signature(h_prime, s_prime), r)
|
||||
}
|
||||
|
||||
pub fn to_bytes(self) -> [u8; 96] {
|
||||
let mut bytes = [0u8; 96];
|
||||
bytes[..48].copy_from_slice(&self.0.to_affine().to_compressed());
|
||||
bytes[48..].copy_from_slice(&self.1.to_affine().to_compressed());
|
||||
bytes
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Signature> {
|
||||
Signature::try_from(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl Bytable for Signature {
|
||||
fn to_byte_vec(&self) -> Vec<u8> {
|
||||
self.to_bytes().to_vec()
|
||||
}
|
||||
|
||||
fn try_from_byte_slice(slice: &[u8]) -> Result<Self> {
|
||||
Signature::from_bytes(slice)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct BlindedSignature(pub(crate) G1Projective, pub(crate) G1Projective);
|
||||
|
||||
pub struct SignatureShare {
|
||||
signature: Signature,
|
||||
index: SignerIndex,
|
||||
}
|
||||
|
||||
impl SignatureShare {
|
||||
pub fn new(signature: Signature, index: SignerIndex) -> Self {
|
||||
SignatureShare { signature, index }
|
||||
}
|
||||
|
||||
pub fn signature(&self) -> &Signature {
|
||||
&self.signature
|
||||
}
|
||||
|
||||
pub fn index(&self) -> SignerIndex {
|
||||
self.index
|
||||
}
|
||||
|
||||
// pub fn aggregate(shares: &[Self]) -> Result<Signature> {
|
||||
// aggregate_signature_shares(shares)
|
||||
// }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rand::RngCore;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn polynomial_evaluation() {
|
||||
// y = 42 (it should be 42 regardless of x)
|
||||
let poly = Polynomial {
|
||||
coefficients: vec![Scalar::from(42)],
|
||||
};
|
||||
|
||||
assert_eq!(Scalar::from(42), poly.evaluate(&Scalar::from(1)));
|
||||
assert_eq!(Scalar::from(42), poly.evaluate(&Scalar::from(0)));
|
||||
assert_eq!(Scalar::from(42), poly.evaluate(&Scalar::from(10)));
|
||||
|
||||
// y = x + 10, at x = 2 (exp: 12)
|
||||
let poly = Polynomial {
|
||||
coefficients: vec![Scalar::from(10), Scalar::from(1)],
|
||||
};
|
||||
|
||||
assert_eq!(Scalar::from(12), poly.evaluate(&Scalar::from(2)));
|
||||
|
||||
// y = x^4 - 5x^2 + 2x - 3, at x = 3 (exp: 39)
|
||||
let poly = Polynomial {
|
||||
coefficients: vec![
|
||||
(-Scalar::from(3)),
|
||||
Scalar::from(2),
|
||||
(-Scalar::from(5)),
|
||||
Scalar::zero(),
|
||||
Scalar::from(1),
|
||||
],
|
||||
};
|
||||
|
||||
assert_eq!(Scalar::from(39), poly.evaluate(&Scalar::from(3)));
|
||||
|
||||
// empty polynomial
|
||||
let poly = Polynomial {
|
||||
coefficients: vec![],
|
||||
};
|
||||
|
||||
// should always be 0
|
||||
assert_eq!(Scalar::from(0), poly.evaluate(&Scalar::from(1)));
|
||||
assert_eq!(Scalar::from(0), poly.evaluate(&Scalar::from(0)));
|
||||
assert_eq!(Scalar::from(0), poly.evaluate(&Scalar::from(10)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn performing_lagrangian_scalar_interpolation_at_origin() {
|
||||
// x^2 + 3
|
||||
// x, f(x):
|
||||
// 1, 4,
|
||||
// 2, 7,
|
||||
// 3, 12,
|
||||
let points = vec![1, 2, 3];
|
||||
let values = vec![Scalar::from(4), Scalar::from(7), Scalar::from(12)];
|
||||
|
||||
assert_eq!(
|
||||
Scalar::from(3),
|
||||
perform_lagrangian_interpolation_at_origin(&points, &values).unwrap()
|
||||
);
|
||||
|
||||
// x^3 + 3x^2 - 5x + 11
|
||||
// x, f(x):
|
||||
// 1, 10
|
||||
// 2, 21
|
||||
// 3, 50
|
||||
// 4, 103
|
||||
let points = vec![1, 2, 3, 4];
|
||||
let values = vec![
|
||||
Scalar::from(10),
|
||||
Scalar::from(21),
|
||||
Scalar::from(50),
|
||||
Scalar::from(103),
|
||||
];
|
||||
|
||||
assert_eq!(
|
||||
Scalar::from(11),
|
||||
perform_lagrangian_interpolation_at_origin(&points, &values).unwrap()
|
||||
);
|
||||
|
||||
// more points than it is required
|
||||
// x^2 + x + 10
|
||||
// x, f(x)
|
||||
// 1, 12
|
||||
// 2, 16
|
||||
// 3, 22
|
||||
// 4, 30
|
||||
// 5, 40
|
||||
let points = vec![1, 2, 3, 4, 5];
|
||||
let values = vec![
|
||||
Scalar::from(12),
|
||||
Scalar::from(16),
|
||||
Scalar::from(22),
|
||||
Scalar::from(30),
|
||||
Scalar::from(40),
|
||||
];
|
||||
|
||||
assert_eq!(
|
||||
Scalar::from(10),
|
||||
perform_lagrangian_interpolation_at_origin(&points, &values).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hash_g1_sanity_check() {
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut msg1 = [0u8; 1024];
|
||||
rng.fill_bytes(&mut msg1);
|
||||
let mut msg2 = [0u8; 1024];
|
||||
rng.fill_bytes(&mut msg2);
|
||||
|
||||
assert_eq!(hash_g1(msg1), hash_g1(msg1));
|
||||
assert_eq!(hash_g1(msg2), hash_g1(msg2));
|
||||
assert_ne!(hash_g1(msg1), hash_g1(msg2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hash_scalar_sanity_check() {
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut msg1 = [0u8; 1024];
|
||||
rng.fill_bytes(&mut msg1);
|
||||
let mut msg2 = [0u8; 1024];
|
||||
rng.fill_bytes(&mut msg2);
|
||||
|
||||
assert_eq!(hash_to_scalar(msg1), hash_to_scalar(msg1));
|
||||
assert_eq!(hash_to_scalar(msg2), hash_to_scalar(msg2));
|
||||
assert_ne!(hash_to_scalar(msg1), hash_to_scalar(msg2));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user