github.com/hellobchain/third_party@v0.0.0-20230331131523-deb0478a2e52/hyperledger/fabric/idemix/signature.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package idemix
     8  
     9  import (
    10  	"github.com/hellobchain/newcryptosm/ecdsa"
    11  	"github.com/hellobchain/wswlog/wlogging"
    12  	"sort"
    13  
    14  	"github.com/hellobchain/third_party/hyperledger/fabric-amcl/amcl"
    15  	"github.com/hellobchain/third_party/hyperledger/fabric-amcl/amcl/FP256BN"
    16  	"github.com/pkg/errors"
    17  )
    18  
    19  var idemixLogger = wlogging.MustGetLoggerWithoutName()
    20  
    21  // signLabel is the label used in zero-knowledge proof (ZKP) to identify that this ZKP is a signature of knowledge
    22  const signLabel = "sign"
    23  
    24  // A signature that is produced using an Identity Mixer credential is a so-called signature of knowledge
    25  // (for details see C.P.Schnorr "Efficient Identification and Signatures for Smart Cards")
    26  // An Identity Mixer signature is a signature of knowledge that signs a message and proves (in zero-knowledge)
    27  // the knowledge of the user secret (and possibly attributes) signed inside a credential
    28  // that was issued by a certain issuer (referred to with the issuer public key)
    29  // The signature is verified using the message being signed and the public key of the issuer
    30  // Some of the attributes from the credential can be selectvely disclosed or different statements can be proven about
    31  // credential atrributes without diclosing them in the clear
    32  // The difference between a standard signature using X.509 certificates and an Identity Mixer signature is
    33  // the advanced privacy features provided by Identity Mixer (due to zero-knowledge proofs):
    34  //  - Unlinkability of the signatures produced with the same credential
    35  //  - Selective attribute disclosure and predicates over attributes
    36  
    37  // Make a slice of all the attribute indices that will not be disclosed
    38  func hiddenIndices(Disclosure []byte) []int {
    39  	HiddenIndices := make([]int, 0)
    40  	for index, disclose := range Disclosure {
    41  		if disclose == 0 {
    42  			HiddenIndices = append(HiddenIndices, index)
    43  		}
    44  	}
    45  	return HiddenIndices
    46  }
    47  
    48  // NewSignature creates a new idemix signature (Schnorr-type signature)
    49  // The []byte Disclosure steers which attributes are disclosed:
    50  // if Disclosure[i] == 0 then attribute i remains hidden and otherwise it is disclosed.
    51  // We require the revocation handle to remain undisclosed (i.e., Disclosure[rhIndex] == 0).
    52  // We use the zero-knowledge proof by http://eprint.iacr.org/2016/663.pdf, Sec. 4.5 to prove knowledge of a BBS+ signature
    53  func NewSignature(cred *Credential, sk *FP256BN.BIG, Nym *FP256BN.ECP, RNym *FP256BN.BIG, ipk *IssuerPublicKey, Disclosure []byte, msg []byte, rhIndex int, cri *CredentialRevocationInformation, rng *amcl.RAND) (*Signature, error) {
    54  	// Validate inputs
    55  	if cred == nil || sk == nil || Nym == nil || RNym == nil || ipk == nil || rng == nil || cri == nil {
    56  		return nil, errors.Errorf("cannot create idemix signature: received nil input")
    57  	}
    58  
    59  	if rhIndex < 0 || rhIndex >= len(ipk.AttributeNames) || len(Disclosure) != len(ipk.AttributeNames) {
    60  		return nil, errors.Errorf("cannot create idemix signature: received invalid input")
    61  	}
    62  
    63  	if cri.RevocationAlg != int32(ALG_NO_REVOCATION) && Disclosure[rhIndex] == 1 {
    64  		return nil, errors.Errorf("Attribute %d is disclosed but also used as revocation handle attribute, which should remain hidden.", rhIndex)
    65  	}
    66  
    67  	// locate the indices of the attributes to hide and sample randomness for them
    68  	HiddenIndices := hiddenIndices(Disclosure)
    69  
    70  	// Generate required randomness r_1, r_2
    71  	r1 := RandModOrder(rng)
    72  	r2 := RandModOrder(rng)
    73  	// Set r_3 as \frac{1}{r_1}
    74  	r3 := FP256BN.NewBIGcopy(r1)
    75  	r3.Invmodp(GroupOrder)
    76  
    77  	// Sample a nonce
    78  	Nonce := RandModOrder(rng)
    79  
    80  	// Parse credential
    81  	A := EcpFromProto(cred.A)
    82  	B := EcpFromProto(cred.B)
    83  
    84  	// Randomize credential
    85  
    86  	// Compute A' as A^{r_!}
    87  	APrime := FP256BN.G1mul(A, r1)
    88  
    89  	// Compute ABar as A'^{-e} b^{r1}
    90  	ABar := FP256BN.G1mul(B, r1)
    91  	ABar.Sub(FP256BN.G1mul(APrime, FP256BN.FromBytes(cred.E)))
    92  
    93  	// Compute B' as b^{r1} / h_r^{r2}, where h_r is h_r
    94  	BPrime := FP256BN.G1mul(B, r1)
    95  	HRand := EcpFromProto(ipk.HRand)
    96  	// Parse h_{sk} from ipk
    97  	HSk := EcpFromProto(ipk.HSk)
    98  
    99  	BPrime.Sub(FP256BN.G1mul(HRand, r2))
   100  
   101  	S := FP256BN.FromBytes(cred.S)
   102  	E := FP256BN.FromBytes(cred.E)
   103  
   104  	// Compute s' as s - r_2 \cdot r_3
   105  	sPrime := Modsub(S, FP256BN.Modmul(r2, r3, GroupOrder), GroupOrder)
   106  
   107  	// The rest of this function constructs the non-interactive zero knowledge proof
   108  	// that links the signature, the non-disclosed attributes and the nym.
   109  
   110  	// Sample the randomness used to compute the commitment values (aka t-values) for the ZKP
   111  	rSk := RandModOrder(rng)
   112  	re := RandModOrder(rng)
   113  	rR2 := RandModOrder(rng)
   114  	rR3 := RandModOrder(rng)
   115  	rSPrime := RandModOrder(rng)
   116  	rRNym := RandModOrder(rng)
   117  
   118  	rAttrs := make([]*FP256BN.BIG, len(HiddenIndices))
   119  	for i := range HiddenIndices {
   120  		rAttrs[i] = RandModOrder(rng)
   121  	}
   122  
   123  	// First compute the non-revocation proof.
   124  	// The challenge of the ZKP needs to depend on it, as well.
   125  	prover, err := getNonRevocationProver(RevocationAlgorithm(cri.RevocationAlg))
   126  	if err != nil {
   127  		return nil, err
   128  	}
   129  	nonRevokedProofHashData, err := prover.getFSContribution(
   130  		FP256BN.FromBytes(cred.Attrs[rhIndex]),
   131  		rAttrs[sort.SearchInts(HiddenIndices, rhIndex)],
   132  		cri,
   133  		rng,
   134  	)
   135  	if err != nil {
   136  		return nil, errors.Wrap(err, "failed to compute non-revoked proof")
   137  	}
   138  
   139  	// Step 1: First message (t-values)
   140  
   141  	// t1 is related to knowledge of the credential (recall, it is a BBS+ signature)
   142  	t1 := APrime.Mul2(re, HRand, rR2) // A'^{r_E} \cdot h_r^{r_{r2}}
   143  
   144  	// t2: is related to knowledge of the non-disclosed attributes that signed  in (A,B,S,E)
   145  	t2 := FP256BN.G1mul(HRand, rSPrime) // h_r^{r_{s'}}
   146  	t2.Add(BPrime.Mul2(rR3, HSk, rSk))  // B'^{r_{r3}} \cdot h_{sk}^{r_{sk}}
   147  	for i := 0; i < len(HiddenIndices)/2; i++ {
   148  		t2.Add(
   149  			// \cdot h_{2 \cdot i}^{r_{attrs,i}
   150  			EcpFromProto(ipk.HAttrs[HiddenIndices[2*i]]).Mul2(
   151  				rAttrs[2*i],
   152  				EcpFromProto(ipk.HAttrs[HiddenIndices[2*i+1]]),
   153  				rAttrs[2*i+1],
   154  			),
   155  		)
   156  	}
   157  	if len(HiddenIndices)%2 != 0 {
   158  		t2.Add(FP256BN.G1mul(EcpFromProto(ipk.HAttrs[HiddenIndices[len(HiddenIndices)-1]]), rAttrs[len(HiddenIndices)-1]))
   159  	}
   160  
   161  	// t3 is related to the knowledge of the secrets behind the pseudonym, which is also signed in (A,B,S,E)
   162  	t3 := HSk.Mul2(rSk, HRand, rRNym) // h_{sk}^{r_{sk}} \cdot h_r^{r_{rnym}}
   163  
   164  	// Step 2: Compute the Fiat-Shamir hash, forming the challenge of the ZKP.
   165  
   166  	// Compute the Fiat-Shamir hash, forming the challenge of the ZKP.
   167  	// proofData is the data being hashed, it consists of:
   168  	// the signature label
   169  	// 7 elements of G1 each taking 2*FieldBytes+1 bytes
   170  	// one bigint (hash of the issuer public key) of length FieldBytes
   171  	// disclosed attributes
   172  	// message being signed
   173  	// the amount of bytes needed for the nonrevocation proof
   174  	proofData := make([]byte, len([]byte(signLabel))+7*(2*FieldBytes+1)+FieldBytes+len(Disclosure)+len(msg)+ProofBytes[RevocationAlgorithm(cri.RevocationAlg)])
   175  	index := 0
   176  	index = appendBytesString(proofData, index, signLabel)
   177  	index = appendBytesG1(proofData, index, t1)
   178  	index = appendBytesG1(proofData, index, t2)
   179  	index = appendBytesG1(proofData, index, t3)
   180  	index = appendBytesG1(proofData, index, APrime)
   181  	index = appendBytesG1(proofData, index, ABar)
   182  	index = appendBytesG1(proofData, index, BPrime)
   183  	index = appendBytesG1(proofData, index, Nym)
   184  	index = appendBytes(proofData, index, nonRevokedProofHashData)
   185  	copy(proofData[index:], ipk.Hash)
   186  	index = index + FieldBytes
   187  	copy(proofData[index:], Disclosure)
   188  	index = index + len(Disclosure)
   189  	copy(proofData[index:], msg)
   190  	c := HashModOrder(proofData)
   191  
   192  	// add the previous hash and the nonce and hash again to compute a second hash (C value)
   193  	index = 0
   194  	proofData = proofData[:2*FieldBytes]
   195  	index = appendBytesBig(proofData, index, c)
   196  	index = appendBytesBig(proofData, index, Nonce)
   197  	ProofC := HashModOrder(proofData)
   198  
   199  	// Step 3: reply to the challenge message (s-values)
   200  	ProofSSk := Modadd(rSk, FP256BN.Modmul(ProofC, sk, GroupOrder), GroupOrder)             // s_sk = rSK + C \cdot sk
   201  	ProofSE := Modsub(re, FP256BN.Modmul(ProofC, E, GroupOrder), GroupOrder)                // s_e = re + C \cdot E
   202  	ProofSR2 := Modadd(rR2, FP256BN.Modmul(ProofC, r2, GroupOrder), GroupOrder)             // s_r2 = rR2 + C \cdot r2
   203  	ProofSR3 := Modsub(rR3, FP256BN.Modmul(ProofC, r3, GroupOrder), GroupOrder)             // s_r3 = rR3 + C \cdot r3
   204  	ProofSSPrime := Modadd(rSPrime, FP256BN.Modmul(ProofC, sPrime, GroupOrder), GroupOrder) // s_S' = rSPrime + C \cdot sPrime
   205  	ProofSRNym := Modadd(rRNym, FP256BN.Modmul(ProofC, RNym, GroupOrder), GroupOrder)       // s_RNym = rRNym + C \cdot RNym
   206  	ProofSAttrs := make([][]byte, len(HiddenIndices))
   207  	for i, j := range HiddenIndices {
   208  		ProofSAttrs[i] = BigToBytes(
   209  			// s_attrsi = rAttrsi + C \cdot cred.Attrs[j]
   210  			Modadd(rAttrs[i], FP256BN.Modmul(ProofC, FP256BN.FromBytes(cred.Attrs[j]), GroupOrder), GroupOrder),
   211  		)
   212  	}
   213  
   214  	// Compute the revocation part
   215  	nonRevokedProof, err := prover.getNonRevokedProof(ProofC)
   216  	if err != nil {
   217  		return nil, err
   218  	}
   219  
   220  	// We are done. Return signature
   221  	return &Signature{
   222  			APrime:             EcpToProto(APrime),
   223  			ABar:               EcpToProto(ABar),
   224  			BPrime:             EcpToProto(BPrime),
   225  			ProofC:             BigToBytes(ProofC),
   226  			ProofSSk:           BigToBytes(ProofSSk),
   227  			ProofSE:            BigToBytes(ProofSE),
   228  			ProofSR2:           BigToBytes(ProofSR2),
   229  			ProofSR3:           BigToBytes(ProofSR3),
   230  			ProofSSPrime:       BigToBytes(ProofSSPrime),
   231  			ProofSAttrs:        ProofSAttrs,
   232  			Nonce:              BigToBytes(Nonce),
   233  			Nym:                EcpToProto(Nym),
   234  			ProofSRNym:         BigToBytes(ProofSRNym),
   235  			RevocationEpochPk:  cri.EpochPk,
   236  			RevocationPkSig:    cri.EpochPkSig,
   237  			Epoch:              cri.Epoch,
   238  			NonRevocationProof: nonRevokedProof},
   239  		nil
   240  }
   241  
   242  // Ver verifies an idemix signature
   243  // Disclosure steers which attributes it expects to be disclosed
   244  // attributeValues contains the desired attribute values.
   245  // This function will check that if attribute i is disclosed, the i-th attribute equals attributeValues[i].
   246  func (sig *Signature) Ver(Disclosure []byte, ipk *IssuerPublicKey, msg []byte, attributeValues []*FP256BN.BIG, rhIndex int, revPk *ecdsa.PublicKey, epoch int) error {
   247  	// Validate inputs
   248  	if ipk == nil || revPk == nil {
   249  		return errors.Errorf("cannot verify idemix signature: received nil input")
   250  	}
   251  
   252  	if rhIndex < 0 || rhIndex >= len(ipk.AttributeNames) || len(Disclosure) != len(ipk.AttributeNames) {
   253  		return errors.Errorf("cannot verify idemix signature: received invalid input")
   254  	}
   255  
   256  	if sig.NonRevocationProof.RevocationAlg != int32(ALG_NO_REVOCATION) && Disclosure[rhIndex] == 1 {
   257  		return errors.Errorf("Attribute %d is disclosed but is also used as revocation handle, which should remain hidden.", rhIndex)
   258  	}
   259  
   260  	HiddenIndices := hiddenIndices(Disclosure)
   261  
   262  	// Parse signature
   263  	APrime := EcpFromProto(sig.GetAPrime())
   264  	ABar := EcpFromProto(sig.GetABar())
   265  	BPrime := EcpFromProto(sig.GetBPrime())
   266  	Nym := EcpFromProto(sig.GetNym())
   267  	ProofC := FP256BN.FromBytes(sig.GetProofC())
   268  	ProofSSk := FP256BN.FromBytes(sig.GetProofSSk())
   269  	ProofSE := FP256BN.FromBytes(sig.GetProofSE())
   270  	ProofSR2 := FP256BN.FromBytes(sig.GetProofSR2())
   271  	ProofSR3 := FP256BN.FromBytes(sig.GetProofSR3())
   272  	ProofSSPrime := FP256BN.FromBytes(sig.GetProofSSPrime())
   273  	ProofSRNym := FP256BN.FromBytes(sig.GetProofSRNym())
   274  	ProofSAttrs := make([]*FP256BN.BIG, len(sig.GetProofSAttrs()))
   275  
   276  	if len(sig.ProofSAttrs) != len(HiddenIndices) {
   277  		return errors.Errorf("signature invalid: incorrect amount of s-values for AttributeProofSpec")
   278  	}
   279  	for i, b := range sig.ProofSAttrs {
   280  		ProofSAttrs[i] = FP256BN.FromBytes(b)
   281  	}
   282  	Nonce := FP256BN.FromBytes(sig.GetNonce())
   283  
   284  	// Parse issuer public key
   285  	W := Ecp2FromProto(ipk.W)
   286  	HRand := EcpFromProto(ipk.HRand)
   287  	HSk := EcpFromProto(ipk.HSk)
   288  
   289  	// Verify signature
   290  	if APrime.Is_infinity() {
   291  		return errors.Errorf("signature invalid: APrime = 1")
   292  	}
   293  	temp1 := FP256BN.Ate(W, APrime)
   294  	temp2 := FP256BN.Ate(GenG2, ABar)
   295  	temp2.Inverse()
   296  	temp1.Mul(temp2)
   297  	if !FP256BN.Fexp(temp1).Isunity() {
   298  		return errors.Errorf("signature invalid: APrime and ABar don't have the expected structure")
   299  	}
   300  
   301  	// Verify ZK proof
   302  
   303  	// Recover t-values
   304  
   305  	// Recompute t1
   306  	t1 := APrime.Mul2(ProofSE, HRand, ProofSR2)
   307  	temp := FP256BN.NewECP()
   308  	temp.Copy(ABar)
   309  	temp.Sub(BPrime)
   310  	t1.Sub(FP256BN.G1mul(temp, ProofC))
   311  
   312  	// Recompute t2
   313  	t2 := FP256BN.G1mul(HRand, ProofSSPrime)
   314  	t2.Add(BPrime.Mul2(ProofSR3, HSk, ProofSSk))
   315  	for i := 0; i < len(HiddenIndices)/2; i++ {
   316  		t2.Add(EcpFromProto(ipk.HAttrs[HiddenIndices[2*i]]).Mul2(ProofSAttrs[2*i], EcpFromProto(ipk.HAttrs[HiddenIndices[2*i+1]]), ProofSAttrs[2*i+1]))
   317  	}
   318  	if len(HiddenIndices)%2 != 0 {
   319  		t2.Add(FP256BN.G1mul(EcpFromProto(ipk.HAttrs[HiddenIndices[len(HiddenIndices)-1]]), ProofSAttrs[len(HiddenIndices)-1]))
   320  	}
   321  	temp = FP256BN.NewECP()
   322  	temp.Copy(GenG1)
   323  	for index, disclose := range Disclosure {
   324  		if disclose != 0 {
   325  			temp.Add(FP256BN.G1mul(EcpFromProto(ipk.HAttrs[index]), attributeValues[index]))
   326  		}
   327  	}
   328  	t2.Add(FP256BN.G1mul(temp, ProofC))
   329  
   330  	// Recompute t3
   331  	t3 := HSk.Mul2(ProofSSk, HRand, ProofSRNym)
   332  	t3.Sub(Nym.Mul(ProofC))
   333  
   334  	// add contribution from the non-revocation proof
   335  	nonRevokedVer, err := getNonRevocationVerifier(RevocationAlgorithm(sig.NonRevocationProof.RevocationAlg))
   336  	if err != nil {
   337  		return err
   338  	}
   339  
   340  	i := sort.SearchInts(HiddenIndices, rhIndex)
   341  	proofSRh := ProofSAttrs[i]
   342  	nonRevokedProofBytes, err := nonRevokedVer.recomputeFSContribution(sig.NonRevocationProof, ProofC, Ecp2FromProto(sig.RevocationEpochPk), proofSRh)
   343  	if err != nil {
   344  		return err
   345  	}
   346  
   347  	// Recompute challenge
   348  	// proofData is the data being hashed, it consists of:
   349  	// the signature label
   350  	// 7 elements of G1 each taking 2*FieldBytes+1 bytes
   351  	// one bigint (hash of the issuer public key) of length FieldBytes
   352  	// disclosed attributes
   353  	// message that was signed
   354  	proofData := make([]byte, len([]byte(signLabel))+7*(2*FieldBytes+1)+FieldBytes+len(Disclosure)+len(msg)+ProofBytes[RevocationAlgorithm(sig.NonRevocationProof.RevocationAlg)])
   355  	index := 0
   356  	index = appendBytesString(proofData, index, signLabel)
   357  	index = appendBytesG1(proofData, index, t1)
   358  	index = appendBytesG1(proofData, index, t2)
   359  	index = appendBytesG1(proofData, index, t3)
   360  	index = appendBytesG1(proofData, index, APrime)
   361  	index = appendBytesG1(proofData, index, ABar)
   362  	index = appendBytesG1(proofData, index, BPrime)
   363  	index = appendBytesG1(proofData, index, Nym)
   364  	index = appendBytes(proofData, index, nonRevokedProofBytes)
   365  	copy(proofData[index:], ipk.Hash)
   366  	index = index + FieldBytes
   367  	copy(proofData[index:], Disclosure)
   368  	index = index + len(Disclosure)
   369  	copy(proofData[index:], msg)
   370  
   371  	c := HashModOrder(proofData)
   372  	index = 0
   373  	proofData = proofData[:2*FieldBytes]
   374  	index = appendBytesBig(proofData, index, c)
   375  	index = appendBytesBig(proofData, index, Nonce)
   376  
   377  	if *ProofC != *HashModOrder(proofData) {
   378  		// This debug line helps identify where the mismatch happened
   379  		idemixLogger.Debugf("Signature Verification : \n"+
   380  			"	[t1:%v]\n,"+
   381  			"	[t2:%v]\n,"+
   382  			"	[t3:%v]\n,"+
   383  			"	[APrime:%v]\n,"+
   384  			"	[ABar:%v]\n,"+
   385  			"	[BPrime:%v]\n,"+
   386  			"	[Nym:%v]\n,"+
   387  			"	[nonRevokedProofBytes:%v]\n,"+
   388  			"	[ipk.Hash:%v]\n,"+
   389  			"	[Disclosure:%v]\n,"+
   390  			"	[msg:%v]\n,",
   391  			EcpToBytes(t1),
   392  			EcpToBytes(t2),
   393  			EcpToBytes(t3),
   394  			EcpToBytes(APrime),
   395  			EcpToBytes(ABar),
   396  			EcpToBytes(BPrime),
   397  			EcpToBytes(Nym),
   398  			nonRevokedProofBytes,
   399  			ipk.Hash,
   400  			Disclosure,
   401  			msg)
   402  		return errors.Errorf("signature invalid: zero-knowledge proof is invalid")
   403  	}
   404  
   405  	// Signature is valid
   406  	return nil
   407  }