|
| 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 | + amcl "github.com/manudrijvers/amcl/go" |
| 11 | + "github.com/pkg/errors" |
| 12 | +) |
| 13 | + |
| 14 | +// credRequestLabel is the label used in zero-knowledge proof (ZKP) to identify that this ZKP is a credential request |
| 15 | +const credRequestLabel = "credRequest" |
| 16 | + |
| 17 | +// Credential issuance is an interactive protocol between a user and an issuer |
| 18 | +// The issuer takes its secret and public keys and user attribute values as input |
| 19 | +// The user takes the issuer public key and user secret as input |
| 20 | +// The issuance protocol consists of the following steps: |
| 21 | +// 1) The issuer sends a random nonce to the user |
| 22 | +// 2) The user creates a Credential Request using the public key of the issuer, user secret, and the nonce as input |
| 23 | +// The request consists of a commitment to the user secret (can be seen as a public key) and a zero-knowledge proof |
| 24 | +// of knowledge of the user secret key |
| 25 | +// The user sends the credential request to the issuer |
| 26 | +// 3) The issuer verifies the credential request by verifying the zero-knowledge proof |
| 27 | +// If the request is valid, the issuer issues a credential to the user by signing the commitment to the secret key |
| 28 | +// together with the attribute values and sends the credential back to the user |
| 29 | +// 4) The user verifies the issuer's signature and stores the credential that consists of |
| 30 | +// the signature value, a randomness used to create the signature, the user secret, and the attribute values |
| 31 | + |
| 32 | +// NewCredRequest creates a new Credential Request, the first message of the interactive credential issuance protocol (from user to issuer) |
| 33 | +func NewCredRequest(sk *amcl.BIG, credS1 *amcl.BIG, IssuerNonce *amcl.BIG, ipk *IssuerPublicKey, rng *amcl.RAND) *CredRequest { |
| 34 | + HSk := EcpFromProto(ipk.HSk) |
| 35 | + HRand := EcpFromProto(ipk.HRand) |
| 36 | + Nym := HSk.Mul2(sk, HRand, credS1) |
| 37 | + |
| 38 | + // Create ZK Proof |
| 39 | + rSk := RandModOrder(rng) |
| 40 | + rRand := RandModOrder(rng) |
| 41 | + t := HSk.Mul2(rSk, HRand, rRand) |
| 42 | + |
| 43 | + // proofData is the data being hashed, it consists of: |
| 44 | + // the credential request label |
| 45 | + // 3 elements of G1 each taking 2*FieldBytes+1 bytes |
| 46 | + // hash of the issuer public key of length FieldBytes |
| 47 | + // issuer nonce of length FieldBytes |
| 48 | + proofData := make([]byte, len([]byte(credRequestLabel))+3*(2*FieldBytes+1)+2*FieldBytes) |
| 49 | + index := 0 |
| 50 | + index = appendBytesString(proofData, index, credRequestLabel) |
| 51 | + index = appendBytesG1(proofData, index, t) |
| 52 | + index = appendBytesG1(proofData, index, HSk) |
| 53 | + index = appendBytesG1(proofData, index, Nym) |
| 54 | + index = appendBytesBig(proofData, index, IssuerNonce) |
| 55 | + copy(proofData[index:], ipk.Hash) |
| 56 | + |
| 57 | + proofC := HashModOrder(proofData) |
| 58 | + proofS1 := amcl.Modadd(amcl.Modmul(proofC, sk, GroupOrder), rSk, GroupOrder) |
| 59 | + proofS2 := amcl.Modadd(amcl.Modmul(proofC, credS1, GroupOrder), rRand, GroupOrder) |
| 60 | + |
| 61 | + return &CredRequest{EcpToProto(Nym), BigToBytes(IssuerNonce), BigToBytes(proofC), BigToBytes(proofS1), BigToBytes(proofS2)} |
| 62 | +} |
| 63 | + |
| 64 | +// Check cryptographically verifies the credential request |
| 65 | +func (m *CredRequest) Check(ipk *IssuerPublicKey) error { |
| 66 | + Nym := EcpFromProto(m.GetNym()) |
| 67 | + IssuerNonce := amcl.FromBytes(m.GetIssuerNonce()) |
| 68 | + ProofC := amcl.FromBytes(m.GetProofC()) |
| 69 | + ProofS1 := amcl.FromBytes(m.GetProofS1()) |
| 70 | + ProofS2 := amcl.FromBytes(m.GetProofS2()) |
| 71 | + |
| 72 | + HSk := EcpFromProto(ipk.HSk) |
| 73 | + HRand := EcpFromProto(ipk.HRand) |
| 74 | + |
| 75 | + if Nym == nil || IssuerNonce == nil || ProofC == nil || ProofS1 == nil || ProofS2 == nil { |
| 76 | + return errors.Errorf("one of the proof values is undefined") |
| 77 | + } |
| 78 | + |
| 79 | + t := HSk.Mul2(ProofS1, HRand, ProofS2) |
| 80 | + t.Sub(Nym.Mul(ProofC)) |
| 81 | + |
| 82 | + // proofData is the data being hashed, it consists of: |
| 83 | + // the credential request label |
| 84 | + // 3 elements of G1 each taking 2*FieldBytes+1 bytes |
| 85 | + // hash of the issuer public key of length FieldBytes |
| 86 | + // issuer nonce of length FieldBytes |
| 87 | + proofData := make([]byte, len([]byte(credRequestLabel))+3*(2*FieldBytes+1)+2*FieldBytes) |
| 88 | + index := 0 |
| 89 | + index = appendBytesString(proofData, index, credRequestLabel) |
| 90 | + index = appendBytesG1(proofData, index, t) |
| 91 | + index = appendBytesG1(proofData, index, HSk) |
| 92 | + index = appendBytesG1(proofData, index, Nym) |
| 93 | + index = appendBytesBig(proofData, index, IssuerNonce) |
| 94 | + copy(proofData[index:], ipk.Hash) |
| 95 | + |
| 96 | + if !ProofC.Equals(HashModOrder(proofData)) { |
| 97 | + return errors.Errorf("zero knowledge proof is invalid") |
| 98 | + } |
| 99 | + |
| 100 | + return nil |
| 101 | +} |
0 commit comments