diff --git a/idemix/idemix.pb.go b/idemix/idemix.pb.go index 5c13d6987ee..68d014851ed 100644 --- a/idemix/idemix.pb.go +++ b/idemix/idemix.pb.go @@ -15,6 +15,7 @@ It has these top-level messages: Credential CredRequest Signature + NymSignature */ package idemix @@ -445,6 +446,55 @@ func (m *Signature) GetProofSRNym() []byte { return nil } +// NymSignature specifies a signature object that signs a message +// with respect to a pseudonym. It differs from the standard idemix.signature in the fact that +// the standard signature object also proves that the pseudonym is based on a secret certified by +// a CA (issuer), whereas NymSignature only proves that the the owner of the pseudonym +// signed the message +type NymSignature struct { + // ProofC is the Fiat-Shamir challenge of the ZKP + ProofC []byte `protobuf:"bytes,1,opt,name=ProofC,proto3" json:"ProofC,omitempty"` + // ProofSSK is the s-value proving knowledge of the user secret key + ProofSSk []byte `protobuf:"bytes,2,opt,name=ProofSSk,proto3" json:"ProofSSk,omitempty"` + // ProofSRNym is the s-value proving knowledge of the pseudonym secret + ProofSRNym []byte `protobuf:"bytes,3,opt,name=ProofSRNym,proto3" json:"ProofSRNym,omitempty"` + // Nonce is a fresh nonce used for the signature + Nonce []byte `protobuf:"bytes,4,opt,name=Nonce,proto3" json:"Nonce,omitempty"` +} + +func (m *NymSignature) Reset() { *m = NymSignature{} } +func (m *NymSignature) String() string { return proto.CompactTextString(m) } +func (*NymSignature) ProtoMessage() {} +func (*NymSignature) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } + +func (m *NymSignature) GetProofC() []byte { + if m != nil { + return m.ProofC + } + return nil +} + +func (m *NymSignature) GetProofSSk() []byte { + if m != nil { + return m.ProofSSk + } + return nil +} + +func (m *NymSignature) GetProofSRNym() []byte { + if m != nil { + return m.ProofSRNym + } + return nil +} + +func (m *NymSignature) GetNonce() []byte { + if m != nil { + return m.Nonce + } + return nil +} + func init() { proto.RegisterType((*ECP)(nil), "ECP") proto.RegisterType((*ECP2)(nil), "ECP2") @@ -453,46 +503,48 @@ func init() { proto.RegisterType((*Credential)(nil), "Credential") proto.RegisterType((*CredRequest)(nil), "CredRequest") proto.RegisterType((*Signature)(nil), "Signature") + proto.RegisterType((*NymSignature)(nil), "NymSignature") } func init() { proto.RegisterFile("idemix/idemix.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 565 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x5c, 0x54, 0x4d, 0x6f, 0xda, 0x4c, - 0x18, 0xd4, 0xfa, 0x83, 0x84, 0x07, 0xde, 0xbc, 0xd1, 0xa6, 0x8a, 0x56, 0x51, 0x55, 0x51, 0xab, - 0x8a, 0x38, 0x81, 0xe2, 0xfc, 0x02, 0x1b, 0xb9, 0x05, 0x55, 0x42, 0xd6, 0xfa, 0x10, 0xe8, 0xcd, - 0xc0, 0x02, 0x16, 0x18, 0xa7, 0x6b, 0x5b, 0x2a, 0xbf, 0xa3, 0xd7, 0xfe, 0xd3, 0x5e, 0xaa, 0xfd, - 0xb0, 0xb1, 0x39, 0xe1, 0x99, 0xf1, 0xb3, 0x3b, 0xcc, 0x3c, 0x32, 0x3c, 0x24, 0x1b, 0x96, 0x26, - 0xbf, 0xc6, 0xea, 0x67, 0xf4, 0xce, 0xb3, 0x22, 0x73, 0x3e, 0x83, 0x19, 0x4c, 0x42, 0xdc, 0x07, - 0xb4, 0x20, 0x68, 0x80, 0x86, 0x7d, 0x8a, 0x16, 0x02, 0x2d, 0x89, 0xa1, 0xd0, 0xd2, 0xf9, 0x0a, - 0x56, 0x30, 0x09, 0x5d, 0x7c, 0x07, 0xc6, 0xc2, 0xd3, 0x2f, 0x19, 0x0b, 0x4f, 0x62, 0x5f, 0xbf, - 0x66, 0x2c, 0x7c, 0x81, 0x97, 0x1e, 0x31, 0x15, 0x5e, 0x4a, 0x7d, 0xe9, 0x13, 0x4b, 0x63, 0xdf, - 0xf9, 0x63, 0xc0, 0xff, 0xb3, 0x3c, 0x2f, 0x19, 0x0f, 0xcb, 0xd5, 0x31, 0x59, 0x7f, 0x67, 0x67, - 0xfc, 0x0c, 0x77, 0x5e, 0x51, 0xf0, 0x64, 0x55, 0x16, 0x6c, 0x1e, 0xa7, 0x2c, 0x27, 0x68, 0x60, - 0x0e, 0xbb, 0xf4, 0x8a, 0xc5, 0x8f, 0x60, 0x4e, 0xa3, 0x83, 0xbc, 0xac, 0xe7, 0x5a, 0xa3, 0x60, - 0x12, 0x52, 0x41, 0xe0, 0x27, 0xb0, 0xa7, 0x34, 0x3e, 0x6d, 0xe4, 0xb5, 0x95, 0xa2, 0x28, 0xfc, - 0x11, 0x3a, 0x53, 0x71, 0x4c, 0x4e, 0xac, 0x81, 0x59, 0x8b, 0x9a, 0xc3, 0x0f, 0x80, 0xde, 0x88, - 0x2d, 0xa7, 0x6c, 0x21, 0xb8, 0x14, 0xbd, 0x89, 0xe3, 0xfc, 0x98, 0x7f, 0x7b, 0x21, 0x9d, 0xe6, - 0x71, 0x92, 0xaa, 0x34, 0x97, 0xdc, 0x5c, 0x6b, 0x2e, 0x7e, 0x84, 0x4e, 0xc8, 0xb3, 0x6c, 0x3b, - 0x21, 0xb7, 0xf2, 0xef, 0x6a, 0x54, 0xf3, 0x11, 0xe9, 0x36, 0xf8, 0x08, 0x63, 0xb0, 0xa6, 0x71, - 0xbe, 0x27, 0x20, 0x59, 0xf9, 0xec, 0x78, 0xd0, 0x55, 0xe9, 0x88, 0x5c, 0xee, 0xc1, 0x9c, 0x45, - 0x07, 0x1d, 0xb6, 0x78, 0xc4, 0x0e, 0x98, 0xb3, 0xb0, 0x4a, 0xe0, 0x7e, 0x74, 0x15, 0x24, 0x15, - 0xa2, 0xb3, 0x05, 0x98, 0x70, 0xb6, 0x61, 0xa7, 0x22, 0x89, 0x8f, 0x18, 0x03, 0x52, 0x75, 0x55, - 0x66, 0x91, 0x27, 0x38, 0xbf, 0x95, 0x22, 0xf2, 0x45, 0xdb, 0x81, 0xae, 0x0d, 0x05, 0x02, 0x45, - 0xba, 0x34, 0x14, 0xe1, 0x0f, 0x60, 0xab, 0x08, 0xed, 0x81, 0x39, 0xec, 0x53, 0x05, 0x9c, 0xdf, - 0x08, 0x7a, 0xe2, 0x22, 0xca, 0x7e, 0x96, 0x2c, 0x2f, 0x44, 0x3b, 0xf3, 0x73, 0xda, 0xba, 0x4b, - 0x10, 0x78, 0x00, 0x3d, 0xe5, 0x73, 0x9e, 0x9d, 0xd6, 0x4c, 0xaf, 0x4a, 0x93, 0x6a, 0x04, 0x67, - 0xb6, 0x82, 0x23, 0x70, 0xa3, 0xa2, 0x7a, 0xd1, 0x5e, 0x2a, 0x78, 0x51, 0x5c, 0xd9, 0x5e, 0xad, - 0xb8, 0xce, 0x5f, 0x03, 0xba, 0x51, 0xb2, 0x3b, 0xc5, 0x45, 0xc9, 0x99, 0x68, 0xdf, 0x0b, 0x79, - 0x92, 0xb2, 0x96, 0x2d, 0xcd, 0x61, 0x02, 0x96, 0xe7, 0xc7, 0xbc, 0x15, 0x85, 0x64, 0xc4, 0x9c, - 0xaf, 0xe6, 0x9a, 0x2b, 0xa5, 0xb9, 0x86, 0x5f, 0xab, 0xe5, 0xf7, 0x09, 0x6e, 0x95, 0x8d, 0xe8, - 0xa0, 0x6d, 0xd5, 0xf8, 0xe2, 0x38, 0x90, 0x6b, 0x55, 0x3b, 0x0e, 0x2e, 0x53, 0x54, 0x6d, 0x55, - 0x3d, 0x45, 0xdd, 0x86, 0xf6, 0xaa, 0x97, 0xaa, 0xc6, 0xd8, 0x81, 0xbe, 0x3e, 0x5d, 0x39, 0x55, - 0xcb, 0xd5, 0xe2, 0x44, 0xf6, 0x0a, 0xab, 0xfe, 0x40, 0xf6, 0xd7, 0xa4, 0x44, 0xb7, 0xaa, 0x97, - 0x9e, 0x1c, 0xb7, 0xab, 0x46, 0x64, 0x97, 0xfd, 0xeb, 0x2e, 0x3f, 0x01, 0xe8, 0xfb, 0x85, 0xfc, - 0x9f, 0x1c, 0x69, 0x30, 0xfe, 0xf3, 0x8f, 0x2f, 0xbb, 0xa4, 0xd8, 0x97, 0xab, 0xd1, 0x3a, 0x4b, - 0xc7, 0xfb, 0xf3, 0x3b, 0xe3, 0x47, 0xb6, 0xd9, 0x31, 0x3e, 0xde, 0xc6, 0x2b, 0x9e, 0xac, 0xf5, - 0x67, 0x67, 0xd5, 0x91, 0xdf, 0x9d, 0xd7, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xa9, 0xa2, 0x25, - 0x06, 0x8e, 0x04, 0x00, 0x00, + // 591 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x54, 0x4d, 0x6b, 0xdb, 0x4c, + 0x18, 0x64, 0xf5, 0xe1, 0xc4, 0x8f, 0xf5, 0xe6, 0x0d, 0x9b, 0x12, 0x96, 0x50, 0x8a, 0x2b, 0x4a, + 0xc8, 0xc9, 0x21, 0xca, 0x2f, 0x90, 0x8c, 0x5a, 0x87, 0x82, 0x11, 0xd2, 0x21, 0x76, 0x6f, 0xb2, + 0xbd, 0xb6, 0x85, 0x2d, 0x2b, 0x5d, 0x49, 0x10, 0xff, 0x8e, 0x5e, 0xfb, 0x4f, 0x7b, 0x29, 0xfb, + 0xa1, 0x4f, 0xe8, 0xc9, 0x9a, 0x19, 0x3d, 0xfb, 0x8c, 0x67, 0x16, 0xc1, 0x4d, 0xb2, 0xa1, 0x69, + 0xf2, 0xfe, 0x28, 0x7f, 0x26, 0x6f, 0x2c, 0x2b, 0x32, 0xfb, 0x33, 0xe8, 0xfe, 0x34, 0xc0, 0x16, + 0xa0, 0x05, 0x41, 0x63, 0xf4, 0x60, 0x85, 0x68, 0xc1, 0xd1, 0x92, 0x68, 0x12, 0x2d, 0xed, 0xaf, + 0x60, 0xf8, 0xd3, 0xc0, 0xc1, 0x57, 0xa0, 0x2d, 0x5c, 0xf5, 0x92, 0xb6, 0x70, 0x05, 0xf6, 0xd4, + 0x6b, 0xda, 0xc2, 0xe3, 0x78, 0xe9, 0x12, 0x5d, 0xe2, 0xa5, 0xd0, 0x97, 0x1e, 0x31, 0x14, 0xf6, + 0xec, 0xdf, 0x1a, 0xfc, 0xff, 0x92, 0xe7, 0x25, 0x65, 0x41, 0xb9, 0x3a, 0x26, 0xeb, 0xef, 0xf4, + 0x8c, 0xef, 0xe1, 0xca, 0x2d, 0x0a, 0x96, 0xac, 0xca, 0x82, 0xce, 0xe3, 0x94, 0xe6, 0x04, 0x8d, + 0xf5, 0x87, 0x61, 0xd8, 0x63, 0xf1, 0x2d, 0xe8, 0xb3, 0xe8, 0x20, 0x96, 0x8d, 0x1c, 0x63, 0xe2, + 0x4f, 0x83, 0x90, 0x13, 0xf8, 0x0e, 0xcc, 0x59, 0x18, 0x9f, 0x36, 0x62, 0x6d, 0xa5, 0x48, 0x0a, + 0x7f, 0x84, 0xc1, 0x8c, 0x1f, 0x93, 0x13, 0x63, 0xac, 0xd7, 0xa2, 0xe2, 0xf0, 0x0d, 0xa0, 0x57, + 0x62, 0x8a, 0x29, 0x93, 0x0b, 0x4e, 0x88, 0x5e, 0xf9, 0x71, 0x5e, 0xcc, 0xbe, 0x3d, 0x91, 0x41, + 0xfb, 0x38, 0x41, 0x55, 0x9a, 0x43, 0x2e, 0xfa, 0x9a, 0x83, 0x6f, 0x61, 0x10, 0xb0, 0x2c, 0xdb, + 0x4e, 0xc9, 0xa5, 0xf8, 0xbb, 0x0a, 0xd5, 0x7c, 0x44, 0x86, 0x2d, 0x3e, 0xc2, 0x18, 0x8c, 0x59, + 0x9c, 0xef, 0x09, 0x08, 0x56, 0x3c, 0xdb, 0x2e, 0x0c, 0x65, 0x3a, 0x3c, 0x97, 0x6b, 0xd0, 0x5f, + 0xa2, 0x83, 0x0a, 0x9b, 0x3f, 0x62, 0x1b, 0xf4, 0x97, 0xa0, 0x4a, 0xe0, 0x7a, 0xd2, 0x0b, 0x32, + 0xe4, 0xa2, 0xbd, 0x05, 0x98, 0x32, 0xba, 0xa1, 0xa7, 0x22, 0x89, 0x8f, 0x18, 0x03, 0x92, 0x75, + 0x55, 0x66, 0x91, 0xcb, 0x39, 0xaf, 0x93, 0x22, 0xf2, 0x78, 0xdb, 0xbe, 0xaa, 0x0d, 0xf9, 0x1c, + 0x45, 0xaa, 0x34, 0x14, 0xe1, 0x0f, 0x60, 0xca, 0x08, 0xcd, 0xb1, 0xfe, 0x60, 0x85, 0x12, 0xd8, + 0xbf, 0x10, 0x8c, 0xf8, 0xa2, 0x90, 0xfe, 0x2c, 0x69, 0x5e, 0xf0, 0x76, 0xe6, 0xe7, 0xb4, 0xb3, + 0x8b, 0x13, 0x78, 0x0c, 0x23, 0xe9, 0x73, 0x9e, 0x9d, 0xd6, 0x54, 0x5d, 0x95, 0x36, 0xd5, 0x0a, + 0x4e, 0xef, 0x04, 0x47, 0xe0, 0x42, 0x46, 0xf5, 0xa4, 0xbc, 0x54, 0xb0, 0x51, 0x1c, 0xd1, 0x5e, + 0xad, 0x38, 0xf6, 0x1f, 0x0d, 0x86, 0x51, 0xb2, 0x3b, 0xc5, 0x45, 0xc9, 0x28, 0x6f, 0xdf, 0x0d, + 0x58, 0x92, 0xd2, 0x8e, 0x2d, 0xc5, 0x61, 0x02, 0x86, 0xeb, 0xc5, 0xac, 0x13, 0x85, 0x60, 0xf8, + 0x9c, 0x27, 0xe7, 0xda, 0x57, 0x4a, 0x71, 0x2d, 0xbf, 0x46, 0xc7, 0xef, 0x1d, 0x5c, 0x4a, 0x1b, + 0xd1, 0x41, 0xd9, 0xaa, 0x71, 0xe3, 0xd8, 0x17, 0xd7, 0xaa, 0x76, 0xec, 0x37, 0x53, 0xa1, 0xbc, + 0x55, 0xf5, 0x54, 0xe8, 0xb4, 0xb4, 0x67, 0x75, 0xa9, 0x6a, 0x8c, 0x6d, 0xb0, 0xd4, 0xe9, 0xd2, + 0xa9, 0xbc, 0x5c, 0x1d, 0x8e, 0x67, 0x2f, 0xb1, 0xec, 0x0f, 0x44, 0x7f, 0x6d, 0x8a, 0x77, 0x2b, + 0x7b, 0x19, 0x89, 0x71, 0xb3, 0x6a, 0x44, 0x74, 0x69, 0xf5, 0xbb, 0xfc, 0x04, 0xa0, 0xf6, 0x73, + 0xf9, 0x3f, 0x31, 0xd2, 0x62, 0xec, 0x77, 0xb0, 0xe6, 0xe7, 0xb4, 0xc9, 0xbf, 0x49, 0x0a, 0xfd, + 0x33, 0x29, 0xad, 0x97, 0x54, 0x77, 0x87, 0xde, 0xdf, 0xd1, 0x38, 0x36, 0x5a, 0x8e, 0xbd, 0xfb, + 0x1f, 0x5f, 0x76, 0x49, 0xb1, 0x2f, 0x57, 0x93, 0x75, 0x96, 0x3e, 0xee, 0xcf, 0x6f, 0x94, 0x1d, + 0xe9, 0x66, 0x47, 0xd9, 0xe3, 0x36, 0x5e, 0xb1, 0x64, 0xad, 0x3e, 0x78, 0xab, 0x81, 0xf8, 0xe2, + 0x3d, 0xff, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x06, 0x88, 0xd9, 0x9e, 0x08, 0x05, 0x00, 0x00, } diff --git a/idemix/idemix_test.go b/idemix/idemix_test.go index 92c7251f00e..263928351ec 100644 --- a/idemix/idemix_test.go +++ b/idemix/idemix_test.go @@ -101,7 +101,8 @@ func TestIdemix(t *testing.T) { disclosure := []byte{0, 0, 0, 0, 0} msg := []byte{1, 2, 3, 4, 5} - sig := NewSignature(cred, sk, Nym, RandNym, key.IPk, disclosure, msg, rng) + sig, err := NewSignature(cred, sk, Nym, RandNym, key.IPk, disclosure, msg, rng) + assert.NoError(t, err) err = sig.Ver(disclosure, key.IPk, msg, nil) if err != nil { @@ -111,11 +112,22 @@ func TestIdemix(t *testing.T) { // Test signing selective disclosure disclosure = []byte{0, 1, 1, 1, 1} - sig = NewSignature(cred, sk, Nym, RandNym, key.IPk, disclosure, msg, rng) + sig, err = NewSignature(cred, sk, Nym, RandNym, key.IPk, disclosure, msg, rng) + assert.NoError(t, err) err = sig.Ver(disclosure, key.IPk, msg, attrs) if err != nil { t.Fatalf("Signature should be valid but verification returned error: %s", err) return } + + // Test NymSignatures + nymsig, err := NewNymSignature(sk, Nym, RandNym, key.IPk, []byte("testing"), rng) + assert.NoError(t, err) + + err = nymsig.Ver(Nym, key.IPk, []byte("testing")) + if err != nil { + t.Fatalf("NymSig should be valid but verification returned error: %s", err) + return + } } diff --git a/idemix/nymsignature.go b/idemix/nymsignature.go new file mode 100644 index 00000000000..c6ab8c5a9a9 --- /dev/null +++ b/idemix/nymsignature.go @@ -0,0 +1,110 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package idemix + +import ( + amcl "github.com/manudrijvers/amcl/go" + "github.com/pkg/errors" +) + +// NewSignature creates a new idemix pseudonym signature +func NewNymSignature(sk *amcl.BIG, Nym *amcl.ECP, RNym *amcl.BIG, ipk *IssuerPublicKey, msg []byte, rng *amcl.RAND) (*NymSignature, error) { + if sk == nil || Nym == nil || RNym == nil || ipk == nil || rng == nil { + return nil, errors.Errorf("cannot create NymSignature: received nil input") + } + + Nonce := RandModOrder(rng) + + HRand := EcpFromProto(ipk.HRand) + HSk := EcpFromProto(ipk.HSk) + + // The rest of this function constructs the non-interactive zero knowledge proof proving that + // the signer 'owns' this pseudonym, i.e., it knows the secret key and randomness on which it is based. + + // take the randomness used to compute the commitment values (aka t-values) for the ZKP + rSk := RandModOrder(rng) + rRNym := RandModOrder(rng) + + // Compute the commitment (aka t-value) + t := HSk.Mul2(rSk, HRand, rRNym) + + // Next, we compute the Fiat-Shamir hash, forming the challenge of the ZKP. + // proofData will hold the data being hashed, it consists of: + // - the signature label + // - 2 elements of G1 each taking 2*FieldBytes+1 bytes + // - one bigint (hash of the issuer public key) of length FieldBytes + // - disclosed attributes + // - message being signed + proofData := make([]byte, len([]byte(signLabel))+2*(2*FieldBytes+1)+FieldBytes+len(msg)) + index := 0 + index = appendBytesString(proofData, index, signLabel) + index = appendBytesG1(proofData, index, t) + index = appendBytesG1(proofData, index, Nym) + copy(proofData[index:], ipk.Hash) + index = index + FieldBytes + copy(proofData[index:], msg) + c := HashModOrder(proofData) + + // combine the previous hash and the nonce and hash again to compute the final Fiat-Shamir value 'ProofC' + index = 0 + proofData = proofData[:2*FieldBytes] + index = appendBytesBig(proofData, index, c) + index = appendBytesBig(proofData, index, Nonce) + ProofC := HashModOrder(proofData) + + // Finally, we compute the s-values, which form the response answering challenge c + ProofSSk := amcl.Modadd(rSk, amcl.Modmul(ProofC, sk, GroupOrder), GroupOrder) + ProofSRNym := amcl.Modadd(rRNym, amcl.Modmul(ProofC, RNym, GroupOrder), GroupOrder) + + // The signature consists of the Fiat-Shamir hash (ProofC), the s-values (ProofSSk, ProofSRNym), and the nonce. + return &NymSignature{ + BigToBytes(ProofC), + BigToBytes(ProofSSk), + BigToBytes(ProofSRNym), + BigToBytes(Nonce)}, nil +} + +// Ver verifies an idemix NymSignature +func (sig *NymSignature) Ver(nym *amcl.ECP, ipk *IssuerPublicKey, msg []byte) error { + ProofC := amcl.FromBytes(sig.GetProofC()) + ProofSSk := amcl.FromBytes(sig.GetProofSSk()) + ProofSRNym := amcl.FromBytes(sig.GetProofSRNym()) + + Nonce := amcl.FromBytes(sig.GetNonce()) + + HRand := EcpFromProto(ipk.HRand) + HSk := EcpFromProto(ipk.HSk) + + t := HSk.Mul2(ProofSSk, HRand, ProofSRNym) + t.Sub(nym.Mul(ProofC)) + + // proofData is the data being hashed, it consists of: + // the signature label + // 2 elements of G1 each taking 2*FieldBytes+1 bytes + // one bigint (hash of the issuer public key) of length FieldBytes + // disclosed attributes + // message being signed + proofData := make([]byte, len([]byte(signLabel))+2*(2*FieldBytes+1)+FieldBytes+len(msg)) + index := 0 + index = appendBytesString(proofData, index, signLabel) + index = appendBytesG1(proofData, index, t) + index = appendBytesG1(proofData, index, nym) + copy(proofData[index:], ipk.Hash) + index = index + FieldBytes + copy(proofData[index:], msg) + + c := HashModOrder(proofData) + index = 0 + proofData = proofData[:2*FieldBytes] + index = appendBytesBig(proofData, index, c) + index = appendBytesBig(proofData, index, Nonce) + if !ProofC.Equals(HashModOrder(proofData)) { + return errors.Errorf("pseudonym signature invalid: zero-knowledge proof is invalid") + } + + return nil +} diff --git a/idemix/signature.go b/idemix/signature.go index 61e58d70ca5..30f16268eb8 100644 --- a/idemix/signature.go +++ b/idemix/signature.go @@ -42,7 +42,11 @@ func hiddenIndices(Disclosure []byte) []int { // The []byte Disclosure steers which attributes are disclosed: // if Disclosure[i] == 0 then attribute i remains hidden and otherwise it is disclosed. // We use the zero-knowledge proof by http://eprint.iacr.org/2016/663.pdf to prove knowledge of a BBS+ signature -func NewSignature(cred *Credential, sk *amcl.BIG, Nym *amcl.ECP, RNym *amcl.BIG, ipk *IssuerPublicKey, Disclosure []byte, msg []byte, rng *amcl.RAND) *Signature { +func NewSignature(cred *Credential, sk *amcl.BIG, Nym *amcl.ECP, RNym *amcl.BIG, ipk *IssuerPublicKey, Disclosure []byte, msg []byte, rng *amcl.RAND) (*Signature, error) { + if cred == nil || sk == nil || Nym == nil || RNym == nil || ipk == nil || rng == nil { + return nil, errors.Errorf("cannot create idemix signature: received nil input") + } + HiddenIndices := hiddenIndices(Disclosure) // Start sig @@ -137,19 +141,20 @@ func NewSignature(cred *Credential, sk *amcl.BIG, Nym *amcl.ECP, RNym *amcl.BIG, } return &Signature{ - EcpToProto(APrime), - EcpToProto(ABar), - EcpToProto(BPrime), - BigToBytes(ProofC), - BigToBytes(ProofSSk), - BigToBytes(ProofSE), - BigToBytes(ProofSR2), - BigToBytes(ProofSR3), - BigToBytes(ProofSSPrime), - ProofSAttrs, - BigToBytes(Nonce), - EcpToProto(Nym), - BigToBytes(ProofSRNym)} + EcpToProto(APrime), + EcpToProto(ABar), + EcpToProto(BPrime), + BigToBytes(ProofC), + BigToBytes(ProofSSk), + BigToBytes(ProofSE), + BigToBytes(ProofSR2), + BigToBytes(ProofSR3), + BigToBytes(ProofSSPrime), + ProofSAttrs, + BigToBytes(Nonce), + EcpToProto(Nym), + BigToBytes(ProofSRNym)}, + nil } // Ver verifies an idemix signature diff --git a/msp/idemixmsp.go b/msp/idemixmsp.go index 839647295be..614ec5fb718 100644 --- a/msp/idemixmsp.go +++ b/msp/idemixmsp.go @@ -152,8 +152,14 @@ func (msp *idemixmsp) Setup(conf1 *m.MSPConfig) error { return errors.Wrap(err, "Credential is not cryptographically valid") } + // Create the cryptographic evidence that this identity is valid + proof, err := idemix.NewSignature(cred, sk, Nym, RandNym, ipk, discloseFlags, nil, rng) + if err != nil { + return errors.Wrap(err, "Failed to setup cryptographic proof of identity") + } + // Set up default signer - msp.signer = &idemixSigningIdentity{newIdemixIdentity(msp, Nym, role, ou), rng, cred, sk, RandNym} + msp.signer = &idemixSigningIdentity{newIdemixIdentity(msp, Nym, role, ou, proof), rng, cred, sk, RandNym} return nil } @@ -216,30 +222,46 @@ func (msp *idemixmsp) deserializeIdentityInternal(serializedID []byte) (Identity return nil, errors.Wrap(err, "cannot deserialize the role of the identity") } - return newIdemixIdentity(msp, Nym, role, ou), nil + proof := &idemix.Signature{} + err = proto.Unmarshal(serialized.Proof, proof) + if err != nil { + return nil, errors.Wrap(err, "cannot deserialize the proof of the identity") + } + + return newIdemixIdentity(msp, Nym, role, ou, proof), nil } func (msp *idemixmsp) Validate(id Identity) error { mspLogger.Infof("Validating identity %s", id) - // NOTE: in idemix, an identity consists of a pseudonym. Validate checks that this pseudonym could be valid - // with respect to this msp, but there is no cryptographic guarantee that a user that can sign wrt this - // pseudonym exists. + var identity *idemixidentity switch t := id.(type) { case *idemixidentity: - if id.(*idemixidentity).GetMSPIdentifier() != msp.name { - return errors.Errorf("the supplied identity does not belong to this msp") - } - return msp.ipk.Check() + identity = id.(*idemixidentity) case *idemixSigningIdentity: - if id.(*idemixSigningIdentity).GetMSPIdentifier() != msp.name { - return errors.Errorf("the supplied identity does not belong to this msp") - } - return msp.ipk.Check() + identity = id.(*idemixSigningIdentity).idemixidentity default: return errors.Errorf("identity type %T is not recognized", t) } + if identity.GetMSPIdentifier() != msp.name { + return errors.Errorf("the supplied identity does not belong to this msp") + } + return identity.verifyProof() +} + +func (id *idemixidentity) verifyProof() error { + ouBytes, err := proto.Marshal(id.OU) + if err != nil { + return errors.Wrapf(err, "error marshalling OU of identity %s", id.GetIdentifier()) + } + roleBytes, err := proto.Marshal(id.Role) + if err != nil { + return errors.Wrapf(err, "error marshalling Role of identity %s", id.GetIdentifier()) + } + attributeValues := []*amcl.BIG{idemix.HashModOrder(ouBytes), idemix.HashModOrder(roleBytes)} + + return id.associationProof.Ver(discloseFlags, id.msp.ipk, nil, attributeValues) } func (msp *idemixmsp) SatisfiesPrincipal(id Identity, principal *m.MSPPrincipal) error { @@ -356,15 +378,20 @@ type idemixidentity struct { id *IdentityIdentifier Role *m.MSPRole OU *m.OrganizationUnit + // associationProof contains cryptographic proof that this identity + // belongs to the MSP id.msp, i.e., it proves that the pseudonym + // is constructed from a secret key on which the CA issued a credential. + associationProof *idemix.Signature } -func newIdemixIdentity(msp *idemixmsp, nym *amcl.ECP, role *m.MSPRole, ou *m.OrganizationUnit) *idemixidentity { +func newIdemixIdentity(msp *idemixmsp, nym *amcl.ECP, role *m.MSPRole, ou *m.OrganizationUnit, proof *idemix.Signature) *idemixidentity { id := &idemixidentity{} id.Nym = nym id.msp = msp id.id = &IdentityIdentifier{Mspid: msp.name, Id: nym.ToString()} id.Role = role id.OU = ou + id.associationProof = proof return id } @@ -403,21 +430,13 @@ func (id *idemixidentity) Verify(msg []byte, sig []byte) error { mspIdentityLogger.Debugf("Verify Idemix sig: sig = %s", hex.Dump(sig)) } - signature := new(idemix.Signature) + signature := new(idemix.NymSignature) err := proto.Unmarshal(sig, signature) if err != nil { return errors.Wrap(err, "error unmarshalling signature") } - ouBytes, err := proto.Marshal(id.OU) - if err != nil { - return errors.Wrapf(err, "error marshalling OU of identity %s", id.GetIdentifier()) - } - roleBytes, err := proto.Marshal(id.Role) - if err != nil { - return errors.Wrapf(err, "error marshalling Role of identity %s", id.GetIdentifier()) - } - attributeValues := []*amcl.BIG{idemix.HashModOrder(ouBytes), idemix.HashModOrder(roleBytes)} - return signature.Ver(discloseFlags, id.msp.ipk, msg, attributeValues) + + return signature.Ver(id.Nym, id.msp.ipk, msg) } func (id *idemixidentity) SatisfiesPrincipal(principal *m.MSPPrincipal) error { @@ -441,6 +460,11 @@ func (id *idemixidentity) Serialize() ([]byte, error) { serialized.OU = ouBytes serialized.Role = roleBytes + serialized.Proof, err = proto.Marshal(id.associationProof) + if err != nil { + return nil, err + } + idemixIDBytes, err := proto.Marshal(serialized) if err != nil { return nil, err @@ -465,7 +489,11 @@ type idemixSigningIdentity struct { func (id *idemixSigningIdentity) Sign(msg []byte) ([]byte, error) { mspLogger.Debugf("Idemix identity %s is signing", id.GetIdentifier()) - return proto.Marshal(idemix.NewSignature(id.Cred, id.Sk, id.Nym, id.RandNym, id.msp.ipk, discloseFlags, msg, id.rng)) + sig, err := idemix.NewNymSignature(id.Sk, id.Nym, id.RandNym, id.msp.ipk, msg, id.rng) + if err != nil { + return nil, err + } + return proto.Marshal(sig) } func (id *idemixSigningIdentity) GetPublicVersion() Identity { diff --git a/msp/idemixmsp_test.go b/msp/idemixmsp_test.go index a8b59adfc2b..ca756519991 100644 --- a/msp/idemixmsp_test.go +++ b/msp/idemixmsp_test.go @@ -19,7 +19,7 @@ import ( func setup(configPath string) (MSP, error) { msp, err := newIdemixMsp() if err != nil { - return nil, errors.Wrap(err, "Getting MSP failed") + return nil, err } conf, err := GetIdemixMspConfig(configPath) @@ -123,10 +123,7 @@ func TestSigning(t *testing.T) { msg := []byte("TestMessage") sig, err := id.Sign(msg) - if err != nil { - t.Fatalf("Signing failed: %s", err) - return - } + assert.NoError(t, err) err = id.Verify(msg, sig) assert.NoError(t, err) @@ -165,28 +162,15 @@ func TestIdentitySerialization(t *testing.T) { // Test serialization of identities serializedID, err := id.Serialize() - if err != nil { - t.Fatalf("Serialize signing identity should have succeeded") - return - } + assert.NoError(t, err) verID, err := msp.DeserializeIdentity(serializedID) - if err != nil { - t.Fatalf("DeserializeIdentity should have succeeded for signing identity but gave error %s", err) - return - } err = verID.Validate() - if err != nil { - t.Fatalf("Id should be valid but gave error %s", err) - return - } + assert.NoError(t, err) err = msp.Validate(verID) - if err != nil { - t.Fatalf("Id should be valid but gave error %s", err) - return - } + assert.NoError(t, err) } func TestIdentitySerializationBad(t *testing.T) { @@ -194,10 +178,7 @@ func TestIdentitySerializationBad(t *testing.T) { assert.NoError(t, err) _, err = msp.DeserializeIdentity([]byte("barf")) - if err == nil { - t.Fatalf("DeserializeIdentity should have failed for bad input") - return - } + assert.Error(t, err, "DeserializeIdentity should have failed for bad input") } func TestIdentitySerializationWrongMSP(t *testing.T) { @@ -212,10 +193,7 @@ func TestIdentitySerializationWrongMSP(t *testing.T) { assert.NoError(t, err) _, err = msp1.DeserializeIdentity(idBytes) - if err == nil { - t.Fatalf("DeserializeIdentity should have failed for ID of other MSP") - return - } + assert.Error(t, err, "DeserializeIdentity should have failed for ID of other MSP") } func TestPrincipalIdentity(t *testing.T) { @@ -233,9 +211,7 @@ func TestPrincipalIdentity(t *testing.T) { Principal: idBytes} err = id1.SatisfiesPrincipal(principal) - if err != nil { - t.Fatalf("Identity MSP principal failed: %s", err) - } + assert.NoError(t, err) } func TestPrincipalIdentityWrongIdentity(t *testing.T) { @@ -259,10 +235,7 @@ func TestPrincipalIdentityWrongIdentity(t *testing.T) { Principal: idBytes} err = id2.SatisfiesPrincipal(principal) - if err == nil { - t.Fatalf("Identity MSP principal for different user should fail") - return - } + assert.Error(t, err, "Identity MSP principal for different user should fail") } func TestPrincipalIdentityBadIdentity(t *testing.T) { @@ -279,10 +252,7 @@ func TestPrincipalIdentityBadIdentity(t *testing.T) { Principal: idBytes} err = id1.SatisfiesPrincipal(principal) - if err == nil { - t.Fatalf("Identity MSP principal for a bad principal should fail") - return - } + assert.Error(t, err, "Identity MSP principal for a bad principal should fail") } func TestIdemixIsWellFormed(t *testing.T) { @@ -325,9 +295,7 @@ func TestPrincipalOU(t *testing.T) { Principal: bytes} err = id1.SatisfiesPrincipal(principal) - if err != nil { - t.Fatalf("OU MSP principal failed: %s", err) - } + assert.NoError(t, err) } func TestPrincipalOUWrongOU(t *testing.T) { @@ -350,9 +318,7 @@ func TestPrincipalOUWrongOU(t *testing.T) { Principal: bytes} err = id1.SatisfiesPrincipal(principal) - if err == nil { - t.Fatalf("OU MSP principal should have failed for user of different OU") - } + assert.Error(t, err, "OU MSP principal should have failed for user of different OU") } func TestPrincipalOUWrongMSP(t *testing.T) { @@ -375,9 +341,7 @@ func TestPrincipalOUWrongMSP(t *testing.T) { Principal: bytes} err = id1.SatisfiesPrincipal(principal) - if err == nil { - t.Fatalf("OU MSP principal should have failed for user of different MSP") - } + assert.Error(t, err, "OU MSP principal should have failed for user of different MSP") } func TestPrincipalOUBad(t *testing.T) { @@ -395,9 +359,7 @@ func TestPrincipalOUBad(t *testing.T) { Principal: bytes} err = id1.SatisfiesPrincipal(principal) - if err == nil { - t.Fatalf("OU MSP principal should have failed for a bad OU principal") - } + assert.Error(t, err, "OU MSP principal should have failed for a bad OU principal") } func TestPrincipalRoleMember(t *testing.T) { @@ -415,9 +377,7 @@ func TestPrincipalRoleMember(t *testing.T) { Principal: principalBytes} err = id1.SatisfiesPrincipal(principal) - if err != nil { - t.Fatalf("Role MSP principal failed: %s", err) - } + assert.NoError(t, err) } func TestPrincipalRoleAdmin(t *testing.T) { @@ -436,10 +396,7 @@ func TestPrincipalRoleAdmin(t *testing.T) { // Admin should also satisfy member err = id1.SatisfiesPrincipal(principal) - if err != nil { - t.Fatalf("Admin should satisfy Role=Member principal but returned error: %s", err) - return - } + assert.NoError(t, err) principalBytes, err = proto.Marshal(&msp.MSPRole{Role: msp.MSPRole_ADMIN, MspIdentifier: id1.GetMSPIdentifier()}) assert.NoError(t, err) @@ -449,9 +406,7 @@ func TestPrincipalRoleAdmin(t *testing.T) { Principal: principalBytes} err = id1.SatisfiesPrincipal(principal) - if err != nil { - t.Fatalf("Admin should satisfy Role=Admin principal but returned error: %s", err) - } + assert.NoError(t, err) } func TestPrincipalRoleNotAdmin(t *testing.T) { @@ -469,9 +424,7 @@ func TestPrincipalRoleNotAdmin(t *testing.T) { Principal: principalBytes} err = id1.SatisfiesPrincipal(principal) - if err == nil { - t.Fatalf("Member should not satisfy Admin principal") - } + assert.Error(t, err, "Member should not satisfy Admin principal") } func TestPrincipalRoleWrongMSP(t *testing.T) { @@ -489,9 +442,7 @@ func TestPrincipalRoleWrongMSP(t *testing.T) { Principal: principalBytes} err = id1.SatisfiesPrincipal(principal) - if err == nil { - t.Fatalf("Role MSP principal should have failed for user of different MSP") - } + assert.Error(t, err, "Role MSP principal should have failed for user of different MSP") } func TestPrincipalRoleBadRole(t *testing.T) { @@ -510,9 +461,7 @@ func TestPrincipalRoleBadRole(t *testing.T) { Principal: principalBytes} err = id1.SatisfiesPrincipal(principal) - if err == nil { - t.Fatalf("Role MSP principal should have failed for a bad Role") - } + assert.Error(t, err, "Role MSP principal should have failed for a bad Role") } func TestPrincipalBad(t *testing.T) { @@ -527,7 +476,5 @@ func TestPrincipalBad(t *testing.T) { Principal: nil} err = id1.SatisfiesPrincipal(principal) - if err == nil { - t.Fatalf("Principal with bad Classification should fail") - } + assert.Error(t, err, "Principal with bad Classification should fail") } diff --git a/protos/idemix/idemix.proto b/protos/idemix/idemix.proto index 0e305025dbf..e4fcbc77842 100644 --- a/protos/idemix/idemix.proto +++ b/protos/idemix/idemix.proto @@ -102,3 +102,19 @@ message Signature { ECP Nym = 12; bytes ProofSRNym = 13; } + +// NymSignature specifies a signature object that signs a message +// with respect to a pseudonym. It differs from the standard idemix.signature in the fact that +// the standard signature object also proves that the pseudonym is based on a secret certified by +// a CA (issuer), whereas NymSignature only proves that the the owner of the pseudonym +// signed the message +message NymSignature { + // ProofC is the Fiat-Shamir challenge of the ZKP + bytes ProofC = 1; + // ProofSSK is the s-value proving knowledge of the user secret key + bytes ProofSSk = 2; + //ProofSRNym is the s-value proving knowledge of the pseudonym secret + bytes ProofSRNym = 3; + // Nonce is a fresh nonce used for the signature + bytes Nonce = 4; +} \ No newline at end of file diff --git a/protos/msp/identities.pb.go b/protos/msp/identities.pb.go index ed7b1058ff6..8a0de69781f 100644 --- a/protos/msp/identities.pb.go +++ b/protos/msp/identities.pb.go @@ -79,7 +79,7 @@ type SerializedIdemixIdentity struct { // It is a []byte representation of an amcl.BIG // The pseudonym can be seen as a public key of the identity, it is used to verify signatures. NymX []byte `protobuf:"bytes,1,opt,name=NymX,proto3" json:"NymX,omitempty"` - // NymX is the Y-component of the pseudonym elliptic curve point. + // NymY is the Y-component of the pseudonym elliptic curve point. // It is a []byte representation of an amcl.BIG // The pseudonym can be seen as a public key of the identity, it is used to verify signatures. NymY []byte `protobuf:"bytes,2,opt,name=NymY,proto3" json:"NymY,omitempty"` @@ -87,6 +87,8 @@ type SerializedIdemixIdentity struct { OU []byte `protobuf:"bytes,3,opt,name=OU,proto3" json:"OU,omitempty"` // Role contains the role of this identity (e.g., ADMIN or MEMBER) Role []byte `protobuf:"bytes,4,opt,name=Role,proto3" json:"Role,omitempty"` + // Proof contains the cryptographic evidence that this identity is valid + Proof []byte `protobuf:"bytes,5,opt,name=Proof,proto3" json:"Proof,omitempty"` } func (m *SerializedIdemixIdentity) Reset() { *m = SerializedIdemixIdentity{} } @@ -122,6 +124,13 @@ func (m *SerializedIdemixIdentity) GetRole() []byte { return nil } +func (m *SerializedIdemixIdentity) GetProof() []byte { + if m != nil { + return m.Proof + } + return nil +} + func init() { proto.RegisterType((*SerializedIdentity)(nil), "msp.SerializedIdentity") proto.RegisterType((*SerializedIdemixIdentity)(nil), "msp.SerializedIdemixIdentity") @@ -130,19 +139,20 @@ func init() { func init() { proto.RegisterFile("msp/identities.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 223 bytes of a gzipped FileDescriptorProto + // 236 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x54, 0x8f, 0x4f, 0x4b, 0x03, 0x31, - 0x10, 0x47, 0xd9, 0x6d, 0xfd, 0x17, 0x8a, 0x87, 0xd0, 0x43, 0xbc, 0xd5, 0x9e, 0xf6, 0x94, 0x1c, - 0xfc, 0x06, 0x05, 0x0f, 0x1e, 0xb4, 0xb0, 0x52, 0x50, 0x2f, 0xd2, 0x6d, 0xa6, 0xdb, 0x81, 0x1d, - 0x13, 0x32, 0x11, 0x8c, 0x9f, 0x5e, 0x36, 0x59, 0x44, 0x6f, 0x6f, 0x7e, 0x3c, 0x1e, 0x8c, 0x58, - 0x12, 0x7b, 0x83, 0x16, 0x3e, 0x22, 0x46, 0x04, 0xd6, 0x3e, 0xb8, 0xe8, 0xe4, 0x8c, 0xd8, 0xaf, - 0xef, 0x85, 0x7c, 0x86, 0x80, 0xfb, 0x01, 0xbf, 0xc1, 0x3e, 0x14, 0x25, 0xc9, 0xa5, 0x38, 0x23, - 0xf6, 0x68, 0x55, 0xb5, 0xaa, 0x9a, 0xab, 0xb6, 0x1c, 0xf2, 0x46, 0x5c, 0xa2, 0x7d, 0xef, 0x52, - 0x04, 0x56, 0xf5, 0xaa, 0x6a, 0x16, 0xed, 0x05, 0xda, 0xcd, 0x78, 0xae, 0x8f, 0x42, 0xfd, 0xcb, - 0x10, 0x7e, 0xfd, 0xc6, 0xa4, 0x98, 0x3f, 0x25, 0x7a, 0xc9, 0xad, 0x45, 0x9b, 0x79, 0xda, 0x5e, - 0xa7, 0x4c, 0x66, 0x79, 0x2d, 0xea, 0xed, 0x4e, 0xcd, 0xf2, 0x52, 0x6f, 0x77, 0xa3, 0xd3, 0xba, - 0x01, 0xd4, 0xbc, 0x38, 0x23, 0x6f, 0x1e, 0xc5, 0xad, 0x0b, 0xbd, 0x3e, 0x25, 0x0f, 0x61, 0x00, - 0xdb, 0x43, 0xd0, 0xc7, 0x7d, 0x17, 0xf0, 0x50, 0x7e, 0x62, 0x4d, 0xec, 0xdf, 0x9a, 0x1e, 0xe3, - 0xe9, 0xb3, 0xd3, 0x07, 0x47, 0xe6, 0x8f, 0x69, 0x8a, 0x69, 0x8a, 0x69, 0x88, 0x7d, 0x77, 0x9e, - 0xf9, 0xee, 0x27, 0x00, 0x00, 0xff, 0xff, 0x4e, 0x90, 0x2e, 0x02, 0x21, 0x01, 0x00, 0x00, + 0x10, 0xc5, 0xd9, 0x6d, 0xeb, 0x9f, 0x50, 0x3c, 0x84, 0x1e, 0xe2, 0xad, 0xf6, 0xb4, 0xa7, 0xe4, + 0xe0, 0x37, 0x28, 0x78, 0xf0, 0xa0, 0x95, 0x95, 0x82, 0x7a, 0x91, 0x6e, 0x33, 0xdd, 0x0e, 0xec, + 0x98, 0x90, 0x89, 0xe0, 0x8a, 0x1f, 0x5e, 0x36, 0x59, 0xc4, 0xde, 0xde, 0x7b, 0xfc, 0xf8, 0x31, + 0x23, 0x16, 0xc4, 0xde, 0xa0, 0x85, 0x8f, 0x88, 0x11, 0x81, 0xb5, 0x0f, 0x2e, 0x3a, 0x39, 0x21, + 0xf6, 0xab, 0x3b, 0x21, 0x9f, 0x21, 0xe0, 0xae, 0xc3, 0x6f, 0xb0, 0xf7, 0x19, 0xe9, 0xe5, 0x42, + 0xcc, 0x88, 0x3d, 0x5a, 0x55, 0x2c, 0x8b, 0xea, 0xb2, 0xce, 0x45, 0x5e, 0x8b, 0x0b, 0xb4, 0xef, + 0x4d, 0x1f, 0x81, 0x55, 0xb9, 0x2c, 0xaa, 0x79, 0x7d, 0x8e, 0x76, 0x3d, 0xd4, 0xd5, 0x8f, 0x50, + 0x27, 0x1a, 0xc2, 0xaf, 0x3f, 0x99, 0x14, 0xd3, 0xc7, 0x9e, 0x5e, 0x92, 0x6b, 0x5e, 0xa7, 0x3c, + 0x6e, 0xaf, 0xa3, 0x26, 0x65, 0x79, 0x25, 0xca, 0xcd, 0x56, 0x4d, 0xd2, 0x52, 0x6e, 0xb6, 0x03, + 0x53, 0xbb, 0x0e, 0xd4, 0x34, 0x33, 0x43, 0x1e, 0x0e, 0x7b, 0x0a, 0xce, 0x1d, 0xd4, 0x2c, 0x8d, + 0xb9, 0xac, 0x1f, 0xc4, 0x8d, 0x0b, 0xad, 0x3e, 0xf6, 0x1e, 0x42, 0x07, 0xb6, 0x85, 0xa0, 0x0f, + 0xbb, 0x26, 0xe0, 0x3e, 0x7f, 0xca, 0x9a, 0xd8, 0xbf, 0x55, 0x2d, 0xc6, 0xe3, 0x67, 0xa3, 0xf7, + 0x8e, 0xcc, 0x3f, 0xd2, 0x64, 0xd2, 0x64, 0xd2, 0x10, 0xfb, 0xe6, 0x2c, 0xe5, 0xdb, 0xdf, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x97, 0xeb, 0x66, 0x12, 0x37, 0x01, 0x00, 0x00, } diff --git a/protos/msp/identities.proto b/protos/msp/identities.proto index c404b0bb121..21ef7fa3f3d 100644 --- a/protos/msp/identities.proto +++ b/protos/msp/identities.proto @@ -33,7 +33,7 @@ message SerializedIdemixIdentity { // The pseudonym can be seen as a public key of the identity, it is used to verify signatures. bytes NymX = 1; - // NymX is the Y-component of the pseudonym elliptic curve point. + // NymY is the Y-component of the pseudonym elliptic curve point. // It is a []byte representation of an amcl.BIG // The pseudonym can be seen as a public key of the identity, it is used to verify signatures. bytes NymY = 2; @@ -43,4 +43,7 @@ message SerializedIdemixIdentity { // Role contains the role of this identity (e.g., ADMIN or MEMBER) bytes Role = 4; + + // Proof contains the cryptographic evidence that this identity is valid + bytes Proof = 5; } \ No newline at end of file