Skip to content

Commit

Permalink
Add signature and key abstraction for anoncreds (#17)
Browse files Browse the repository at this point in the history
* add in signature abstraction

Signed-off-by: Michael Lodder <[email protected]>

* add traits for abstraction

Signed-off-by: Michael Lodder <[email protected]>

* fix test issues

Signed-off-by: Michael Lodder <[email protected]>

* lint fix

Signed-off-by: Michael Lodder <[email protected]>

---------

Signed-off-by: Michael Lodder <[email protected]>
  • Loading branch information
mikelodder7 authored Mar 4, 2024
1 parent 48f7136 commit 954ed67
Show file tree
Hide file tree
Showing 15 changed files with 351 additions and 109 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ serde_json = "1"
serde_regex = "1"
sha2 = "0.10"
sha3 = "0.10"
blsful = "2.4"
subtle = "2.4.1"
blsful = "2.5"
subtle = "2.5"
uint-zigzag = { version = "0.2", features = ["std"] }
zeroize = "1"
log = "0.4.20"
Expand Down
21 changes: 20 additions & 1 deletion src/knox/ps/blind_signature.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use super::{SecretKey, Signature};
use super::{PublicKey, SecretKey, Signature};
use crate::knox::short_group_sig_core::short_group_traits::BlindSignature as BlindSignatureTrait;
use crate::CredxResult;
use blsful::inner_types::{G1Projective, Scalar};
use serde::{Deserialize, Serialize};
Expand All @@ -14,6 +15,24 @@ use subtle::CtOption;
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Deserialize, Serialize)]
pub struct BlindSignature(pub(crate) Signature);

impl BlindSignatureTrait for BlindSignature {
type SecretKey = SecretKey;
type PublicKey = PublicKey;
type Signature = Signature;

fn new(
commitment: G1Projective,
sk: &SecretKey,
msgs: &[(usize, Scalar)],
) -> CredxResult<Self> {
Self::new(commitment, sk, msgs)
}

fn to_unblinded(self, blinding: Scalar) -> Signature {
self.to_unblinded(blinding)
}
}

impl BlindSignature {
/// The size of the signature in bytes
pub const BYTES: usize = 128;
Expand Down
16 changes: 11 additions & 5 deletions src/knox/ps/pok_signature.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use super::{PokSignatureProof, PublicKey, Signature};
use crate::knox::short_group_sig_core::*;
use crate::knox::short_group_sig_core::{
short_group_traits::ProofOfSignatureKnowledgeContribution, *,
};
use crate::CredxResult;
use blsful::inner_types::{ff::Field, group::Curve, G1Projective, G2Affine, G2Projective, Scalar};
use merlin::Transcript;
Expand All @@ -15,9 +17,13 @@ pub struct PokSignature {
sigma_2: G1Projective,
}

impl PokSignature {
impl ProofOfSignatureKnowledgeContribution for PokSignature {
type Signature = Signature;
type PublicKey = PublicKey;
type ProofOfKnowledge = PokSignatureProof;

/// Creates the initial proof data before a Fiat-Shamir calculation
pub fn init(
fn commit(
signature: Signature,
public_key: &PublicKey,
messages: &[ProofMessage<Scalar>],
Expand Down Expand Up @@ -73,7 +79,7 @@ impl PokSignature {
}

/// Convert the committed values to bytes for the fiat-shamir challenge
pub fn add_proof_contribution(&mut self, transcript: &mut Transcript) {
fn add_proof_contribution(&self, transcript: &mut Transcript) {
transcript.append_message(
b"sigma_1",
self.sigma_1.to_affine().to_compressed().as_ref(),
Expand All @@ -91,7 +97,7 @@ impl PokSignature {
}

/// Generate the Schnorr challenges for the selective disclosure proofs
pub fn generate_proof(self, challenge: Scalar) -> CredxResult<PokSignatureProof> {
fn generate_proof(self, challenge: Scalar) -> CredxResult<PokSignatureProof> {
let proof = self
.proof
.generate_proof(challenge, self.secrets.as_ref())?;
Expand Down
172 changes: 92 additions & 80 deletions src/knox/ps/pok_signature_proof.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::PublicKey;
use crate::error::Error;
use crate::knox::short_group_sig_core::short_group_traits::ProofOfSignatureKnowledge;
use crate::CredxResult;
use blsful::inner_types::{group::prime::PrimeCurveAffine, *};
use core::convert::TryFrom;
Expand All @@ -17,80 +18,11 @@ pub struct PokSignatureProof {
pub(crate) proof: Vec<Scalar>,
}

impl PokSignatureProof {
/// Store the proof as a sequence of bytes
/// Each point is compressed to big-endian format
/// Needs (N + 2) * 32 + 48 * 2 + 96 space otherwise it will panic
/// where N is the number of hidden messages
pub fn to_bytes(&self) -> Vec<u8> {
let mut buffer = Vec::new();
buffer.extend_from_slice(&self.sigma_1.to_affine().to_compressed());
buffer.extend_from_slice(&self.sigma_2.to_affine().to_compressed());
buffer.extend_from_slice(&self.commitment.to_affine().to_compressed());

for m in &self.proof {
buffer.extend_from_slice(m.to_be_bytes().as_ref());
}
buffer
}

/// Convert a byte sequence into the blind signature context
/// Expected size is (N + 2) * 32 + 48 * 2 bytes
pub fn from_bytes<B: AsRef<[u8]>>(bytes: B) -> Option<Self> {
const SIZE: usize = 32 * 3 + 48 * 4;
let buffer = bytes.as_ref();
if buffer.len() < SIZE {
return None;
}
if buffer.len() % 32 != 0 {
return None;
}

let hid_msg_cnt = (buffer.len() - 48 * 4) / 32;
let mut offset = 48;
let mut end = 96;
let sigma_1 = G1Affine::from_compressed(&<[u8; 48]>::try_from(&buffer[..offset]).unwrap())
.map(G1Projective::from);
let sigma_2 =
G1Affine::from_compressed(&<[u8; 48]>::try_from(&buffer[offset..end]).unwrap())
.map(G1Projective::from);
offset = end;
end += 96;
let commitment =
G2Affine::from_compressed(&<[u8; 96]>::try_from(&buffer[offset..end]).unwrap())
.map(G2Projective::from);

if sigma_1.is_none().unwrap_u8() == 1
|| sigma_2.is_none().unwrap_u8() == 1
|| commitment.is_none().unwrap_u8() == 1
{
return None;
}

offset = end;
end += 32;

let mut proof = Vec::new();
for _ in 0..hid_msg_cnt {
let c = Scalar::from_be_bytes(&<[u8; 32]>::try_from(&buffer[offset..end]).unwrap());
offset = end;
end = offset + 32;
if c.is_none().unwrap_u8() == 1 {
return None;
}

proof.push(c.unwrap());
}
Some(Self {
sigma_1: sigma_1.unwrap(),
sigma_2: sigma_2.unwrap(),
commitment: commitment.unwrap(),
proof,
})
}
impl ProofOfSignatureKnowledge for PokSignatureProof {
type PublicKey = PublicKey;

/// Convert the committed values to bytes for the fiat-shamir challenge
pub fn add_challenge_contribution(
fn add_proof_contribution(
&self,
public_key: &PublicKey,
rvl_msgs: &[(usize, Scalar)],
Expand Down Expand Up @@ -140,7 +72,7 @@ impl PokSignatureProof {
/// Validate the proof, only checks the signature proof
/// the selective disclosure proof is checked by verifying
/// self.challenge == computed_challenge
pub fn verify(&self, rvl_msgs: &[(usize, Scalar)], public_key: &PublicKey) -> bool {
fn verify(&self, rvl_msgs: &[(usize, Scalar)], public_key: &PublicKey) -> CredxResult<()> {
// check the signature proof
if self
.sigma_1
Expand All @@ -149,22 +81,24 @@ impl PokSignatureProof {
.unwrap_u8()
== 1
{
return false;
return Err(Error::General("Invalid proof - identity"));
}

if public_key.y.len() < rvl_msgs.len() {
return false;
return Err(Error::General(
"Invalid key - revealed messages length is bigger than the public key",
));
}
if public_key.is_invalid().unwrap_u8() == 1u8 {
return false;
return Err(Error::General("Invalid public key"));
}

let mut points = Vec::new();
let mut scalars = Vec::new();

for (idx, msg) in rvl_msgs {
if *idx > public_key.y.len() {
return false;
return Err(Error::General("Invalid proof - revealed message index"));
}
points.push(public_key.y[*idx]);
scalars.push(*msg);
Expand All @@ -176,7 +110,7 @@ impl PokSignatureProof {

let j = G2Projective::sum_of_products(points.as_ref(), scalars.as_ref());

multi_miller_loop(&[
let res = multi_miller_loop(&[
(&self.sigma_1.to_affine(), &G2Prepared::from(j.to_affine())),
(
&self.sigma_2.to_affine(),
Expand All @@ -186,11 +120,16 @@ impl PokSignatureProof {
.final_exponentiation()
.is_identity()
.unwrap_u8()
== 1
== 1;
if res {
Ok(())
} else {
Err(Error::General("Invalid proof - signature proof"))
}
}

/// Return the Schnorr proofs for all hidden messages
pub fn get_hidden_message_proofs(
fn get_hidden_message_proofs(
&self,
public_key: &PublicKey,
rvl_msgs: &[(usize, Scalar)],
Expand Down Expand Up @@ -218,3 +157,76 @@ impl PokSignatureProof {
Ok(hidden)
}
}

impl PokSignatureProof {
/// Store the proof as a sequence of bytes
/// Each point is compressed to big-endian format
/// Needs (N + 2) * 32 + 48 * 2 + 96 space otherwise it will panic
/// where N is the number of hidden messages
pub fn to_bytes(&self) -> Vec<u8> {
let mut buffer = Vec::new();
buffer.extend_from_slice(&self.sigma_1.to_affine().to_compressed());
buffer.extend_from_slice(&self.sigma_2.to_affine().to_compressed());
buffer.extend_from_slice(&self.commitment.to_affine().to_compressed());

for m in &self.proof {
buffer.extend_from_slice(m.to_be_bytes().as_ref());
}
buffer
}

/// Convert a byte sequence into the blind signature context
/// Expected size is (N + 2) * 32 + 48 * 2 bytes
pub fn from_bytes<B: AsRef<[u8]>>(bytes: B) -> Option<Self> {
const SIZE: usize = 32 * 3 + 48 * 4;
let buffer = bytes.as_ref();
if buffer.len() < SIZE {
return None;
}
if buffer.len() % 32 != 0 {
return None;
}

let hid_msg_cnt = (buffer.len() - 48 * 4) / 32;
let mut offset = 48;
let mut end = 96;
let sigma_1 = G1Affine::from_compressed(&<[u8; 48]>::try_from(&buffer[..offset]).unwrap())
.map(G1Projective::from);
let sigma_2 =
G1Affine::from_compressed(&<[u8; 48]>::try_from(&buffer[offset..end]).unwrap())
.map(G1Projective::from);
offset = end;
end += 96;
let commitment =
G2Affine::from_compressed(&<[u8; 96]>::try_from(&buffer[offset..end]).unwrap())
.map(G2Projective::from);

if sigma_1.is_none().unwrap_u8() == 1
|| sigma_2.is_none().unwrap_u8() == 1
|| commitment.is_none().unwrap_u8() == 1
{
return None;
}

offset = end;
end += 32;

let mut proof = Vec::new();
for _ in 0..hid_msg_cnt {
let c = Scalar::from_be_bytes(&<[u8; 32]>::try_from(&buffer[offset..end]).unwrap());
offset = end;
end = offset + 32;
if c.is_none().unwrap_u8() == 1 {
return None;
}

proof.push(c.unwrap());
}
Some(Self {
sigma_1: sigma_1.unwrap(),
sigma_2: sigma_2.unwrap(),
commitment: commitment.unwrap(),
proof,
})
}
}
3 changes: 2 additions & 1 deletion src/knox/ps/prover.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use super::{BlindSignatureContext, PokSignature, PublicKey, Signature};
use crate::knox::short_group_sig_core::short_group_traits::ProofOfSignatureKnowledgeContribution;
use crate::knox::short_group_sig_core::*;
use crate::CredxResult;
use blsful::inner_types::{ff::Field, group::Curve, G1Affine, G1Projective, Scalar};
Expand Down Expand Up @@ -71,7 +72,7 @@ impl Prover {
messages: &[ProofMessage<Scalar>],
rng: impl RngCore + CryptoRng,
) -> CredxResult<PokSignature> {
PokSignature::init(signature, public_key, messages, rng)
PokSignature::commit(signature, public_key, messages, rng)
}
}

Expand Down
52 changes: 52 additions & 0 deletions src/knox/ps/public_key.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use super::SecretKey;
use crate::{
error::Error, knox::short_group_sig_core::short_group_traits::PublicKey as PublicKeyTrait,
};
use blsful::inner_types::*;
use core::convert::TryFrom;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -49,6 +52,55 @@ impl From<&SecretKey> for PublicKey {
}
}

impl PublicKeyTrait for PublicKey {
type MessageGenerator = G2Projective;
type BlindMessageGenerator = G1Projective;
}

impl From<PublicKey> for Vec<u8> {
fn from(pk: PublicKey) -> Self {
Self::from(&pk)
}
}

impl From<&PublicKey> for Vec<u8> {
fn from(pk: &PublicKey) -> Self {
pk.to_bytes()
}
}

impl TryFrom<Vec<u8>> for PublicKey {
type Error = Error;

fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
Self::try_from(bytes.as_slice())
}
}

impl TryFrom<&Vec<u8>> for PublicKey {
type Error = Error;

fn try_from(bytes: &Vec<u8>) -> Result<Self, Self::Error> {
Self::try_from(bytes.as_slice())
}
}

impl TryFrom<&[u8]> for PublicKey {
type Error = Error;

fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
Self::from_bytes(bytes).ok_or(Error::General("Invalid public key"))
}
}

impl TryFrom<Box<[u8]>> for PublicKey {
type Error = Error;

fn try_from(bytes: Box<[u8]>) -> Result<Self, Self::Error> {
Self::try_from(bytes.as_ref())
}
}

impl PublicKey {
/// Check if this public key is valid
pub fn is_valid(&self) -> Choice {
Expand Down
Loading

0 comments on commit 954ed67

Please sign in to comment.