Commit ed088b17 authored by Angelo De Caro's avatar Angelo De Caro
Browse files

[FAB-12651] Idemix Nym Signer/Verifier



This change-set does the following:
- implement the nym signer/verifier
- tests

Change-Id: I60dcb8d34dc77ea84fef383d3933c2daf73827bd
Signed-off-by: default avatarAngelo De Caro <adc@zurich.ibm.com>
parent e061a2d2
......@@ -136,3 +136,13 @@ type SignatureScheme interface {
// epoch: revocation epoch.
Verify(ipk IssuerPublicKey, signature, msg []byte, attributes []bccsp.IdemixAttribute, rhIndex int, revocationPublicKey *ecdsa.PublicKey, epoch int) error
}
// NymSignatureScheme is a local interface to decouple from the idemix implementation
// the nym sign-related operations
type NymSignatureScheme interface {
// Sign creates a new idemix pseudonym signature
Sign(sk Big, Nym Ecp, RNym Big, ipk IssuerPublicKey, digest []byte) ([]byte, error)
// Verify verifies an idemix NymSignature
Verify(pk IssuerPublicKey, Nym Ecp, signature, digest []byte) error
}
......@@ -22,6 +22,7 @@ import (
//go:generate counterfeiter -o mock/credential.go -fake-name Credential . Credential
//go:generate counterfeiter -o mock/revocation.go -fake-name Revocation . Revocation
//go:generate counterfeiter -o mock/signature_scheme.go -fake-name SignatureScheme . SignatureScheme
//go:generate counterfeiter -o mock/nymsignature_scheme.go -fake-name NymSignatureScheme . NymSignatureScheme
func TestPlain(t *testing.T) {
RegisterFailHandler(Fail)
......
// Code generated by counterfeiter. DO NOT EDIT.
package mock
import (
"sync"
"github.com/hyperledger/fabric/bccsp/idemix"
)
type NymSignatureScheme struct {
SignStub func(sk idemix.Big, Nym idemix.Ecp, RNym idemix.Big, ipk idemix.IssuerPublicKey, digest []byte) ([]byte, error)
signMutex sync.RWMutex
signArgsForCall []struct {
sk idemix.Big
Nym idemix.Ecp
RNym idemix.Big
ipk idemix.IssuerPublicKey
digest []byte
}
signReturns struct {
result1 []byte
result2 error
}
signReturnsOnCall map[int]struct {
result1 []byte
result2 error
}
VerifyStub func(pk idemix.IssuerPublicKey, Nym idemix.Ecp, signature, digest []byte) error
verifyMutex sync.RWMutex
verifyArgsForCall []struct {
pk idemix.IssuerPublicKey
Nym idemix.Ecp
signature []byte
digest []byte
}
verifyReturns struct {
result1 error
}
verifyReturnsOnCall map[int]struct {
result1 error
}
invocations map[string][][]interface{}
invocationsMutex sync.RWMutex
}
func (fake *NymSignatureScheme) Sign(sk idemix.Big, Nym idemix.Ecp, RNym idemix.Big, ipk idemix.IssuerPublicKey, digest []byte) ([]byte, error) {
var digestCopy []byte
if digest != nil {
digestCopy = make([]byte, len(digest))
copy(digestCopy, digest)
}
fake.signMutex.Lock()
ret, specificReturn := fake.signReturnsOnCall[len(fake.signArgsForCall)]
fake.signArgsForCall = append(fake.signArgsForCall, struct {
sk idemix.Big
Nym idemix.Ecp
RNym idemix.Big
ipk idemix.IssuerPublicKey
digest []byte
}{sk, Nym, RNym, ipk, digestCopy})
fake.recordInvocation("Sign", []interface{}{sk, Nym, RNym, ipk, digestCopy})
fake.signMutex.Unlock()
if fake.SignStub != nil {
return fake.SignStub(sk, Nym, RNym, ipk, digest)
}
if specificReturn {
return ret.result1, ret.result2
}
return fake.signReturns.result1, fake.signReturns.result2
}
func (fake *NymSignatureScheme) SignCallCount() int {
fake.signMutex.RLock()
defer fake.signMutex.RUnlock()
return len(fake.signArgsForCall)
}
func (fake *NymSignatureScheme) SignArgsForCall(i int) (idemix.Big, idemix.Ecp, idemix.Big, idemix.IssuerPublicKey, []byte) {
fake.signMutex.RLock()
defer fake.signMutex.RUnlock()
return fake.signArgsForCall[i].sk, fake.signArgsForCall[i].Nym, fake.signArgsForCall[i].RNym, fake.signArgsForCall[i].ipk, fake.signArgsForCall[i].digest
}
func (fake *NymSignatureScheme) SignReturns(result1 []byte, result2 error) {
fake.SignStub = nil
fake.signReturns = struct {
result1 []byte
result2 error
}{result1, result2}
}
func (fake *NymSignatureScheme) SignReturnsOnCall(i int, result1 []byte, result2 error) {
fake.SignStub = nil
if fake.signReturnsOnCall == nil {
fake.signReturnsOnCall = make(map[int]struct {
result1 []byte
result2 error
})
}
fake.signReturnsOnCall[i] = struct {
result1 []byte
result2 error
}{result1, result2}
}
func (fake *NymSignatureScheme) Verify(pk idemix.IssuerPublicKey, Nym idemix.Ecp, signature []byte, digest []byte) error {
var signatureCopy []byte
if signature != nil {
signatureCopy = make([]byte, len(signature))
copy(signatureCopy, signature)
}
var digestCopy []byte
if digest != nil {
digestCopy = make([]byte, len(digest))
copy(digestCopy, digest)
}
fake.verifyMutex.Lock()
ret, specificReturn := fake.verifyReturnsOnCall[len(fake.verifyArgsForCall)]
fake.verifyArgsForCall = append(fake.verifyArgsForCall, struct {
pk idemix.IssuerPublicKey
Nym idemix.Ecp
signature []byte
digest []byte
}{pk, Nym, signatureCopy, digestCopy})
fake.recordInvocation("Verify", []interface{}{pk, Nym, signatureCopy, digestCopy})
fake.verifyMutex.Unlock()
if fake.VerifyStub != nil {
return fake.VerifyStub(pk, Nym, signature, digest)
}
if specificReturn {
return ret.result1
}
return fake.verifyReturns.result1
}
func (fake *NymSignatureScheme) VerifyCallCount() int {
fake.verifyMutex.RLock()
defer fake.verifyMutex.RUnlock()
return len(fake.verifyArgsForCall)
}
func (fake *NymSignatureScheme) VerifyArgsForCall(i int) (idemix.IssuerPublicKey, idemix.Ecp, []byte, []byte) {
fake.verifyMutex.RLock()
defer fake.verifyMutex.RUnlock()
return fake.verifyArgsForCall[i].pk, fake.verifyArgsForCall[i].Nym, fake.verifyArgsForCall[i].signature, fake.verifyArgsForCall[i].digest
}
func (fake *NymSignatureScheme) VerifyReturns(result1 error) {
fake.VerifyStub = nil
fake.verifyReturns = struct {
result1 error
}{result1}
}
func (fake *NymSignatureScheme) VerifyReturnsOnCall(i int, result1 error) {
fake.VerifyStub = nil
if fake.verifyReturnsOnCall == nil {
fake.verifyReturnsOnCall = make(map[int]struct {
result1 error
})
}
fake.verifyReturnsOnCall[i] = struct {
result1 error
}{result1}
}
func (fake *NymSignatureScheme) Invocations() map[string][][]interface{} {
fake.invocationsMutex.RLock()
defer fake.invocationsMutex.RUnlock()
fake.signMutex.RLock()
defer fake.signMutex.RUnlock()
fake.verifyMutex.RLock()
defer fake.verifyMutex.RUnlock()
copiedInvocations := map[string][][]interface{}{}
for key, value := range fake.invocations {
copiedInvocations[key] = value
}
return copiedInvocations
}
func (fake *NymSignatureScheme) recordInvocation(key string, args []interface{}) {
fake.invocationsMutex.Lock()
defer fake.invocationsMutex.Unlock()
if fake.invocations == nil {
fake.invocations = map[string][][]interface{}{}
}
if fake.invocations[key] == nil {
fake.invocations[key] = [][]interface{}{}
}
fake.invocations[key] = append(fake.invocations[key], args)
}
var _ idemix.NymSignatureScheme = new(NymSignatureScheme)
......@@ -82,6 +82,10 @@ type nymPublicKey struct {
pk Ecp
}
func NewNymPublicKey(pk Ecp) *nymPublicKey {
return &nymPublicKey{pk: pk}
}
func (k *nymPublicKey) Bytes() ([]byte, error) {
return k.pk.Bytes()
}
......
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package idemix
import (
"github.com/hyperledger/fabric/bccsp"
"github.com/pkg/errors"
)
type NymSigner struct {
NymSignatureScheme NymSignatureScheme
}
func (s *NymSigner) Sign(k bccsp.Key, digest []byte, opts bccsp.SignerOpts) ([]byte, error) {
userSecretKey, ok := k.(*userSecretKey)
if !ok {
return nil, errors.New("invalid key, expected *userSecretKey")
}
signerOpts, ok := opts.(*bccsp.IdemixNymSignerOpts)
if !ok {
return nil, errors.New("invalid options, expected *IdemixNymSignerOpts")
}
// Issuer public key
if signerOpts.IssuerPK == nil {
return nil, errors.New("invalid options, missing issuer public key")
}
ipk, ok := signerOpts.IssuerPK.(*issuerPublicKey)
if !ok {
return nil, errors.New("invalid issuer public key, expected *issuerPublicKey")
}
// Nym
if signerOpts.Nym == nil {
return nil, errors.New("invalid options, missing nym key")
}
nymSk, ok := signerOpts.Nym.(*nymSecretKey)
if !ok {
return nil, errors.New("invalid nym key, expected *nymSecretKey")
}
sigma, err := s.NymSignatureScheme.Sign(
userSecretKey.sk,
nymSk.pk, nymSk.sk,
ipk.pk,
digest)
if err != nil {
return nil, err
}
return sigma, nil
}
type NymVerifier struct {
NymSignatureScheme NymSignatureScheme
}
func (v *NymVerifier) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.SignerOpts) (bool, error) {
nymPublicKey, ok := k.(*nymPublicKey)
if !ok {
return false, errors.New("invalid key, expected *nymPublicKey")
}
signerOpts, ok := opts.(*bccsp.IdemixNymSignerOpts)
if !ok {
return false, errors.New("invalid options, expected *IdemixNymSignerOpts")
}
if signerOpts.IssuerPK == nil {
return false, errors.New("invalid options, missing issuer public key")
}
ipk, ok := signerOpts.IssuerPK.(*issuerPublicKey)
if !ok {
return false, errors.New("invalid issuer public key, expected *issuerPublicKey")
}
if len(signature) == 0 {
return false, errors.New("invalid signature, it must not be empty")
}
err := v.NymSignatureScheme.Verify(
ipk.pk,
nymPublicKey.pk,
signature,
digest)
if err != nil {
return false, err
}
return true, nil
}
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package idemix_test
import (
"errors"
"github.com/hyperledger/fabric/bccsp"
"github.com/hyperledger/fabric/bccsp/idemix"
"github.com/hyperledger/fabric/bccsp/idemix/mock"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Nym Signature", func() {
Describe("when creating a signature", func() {
var (
NymSigner *idemix.NymSigner
fakeSignatureScheme *mock.NymSignatureScheme
nymSK bccsp.Key
)
BeforeEach(func() {
fakeSignatureScheme = &mock.NymSignatureScheme{}
NymSigner = &idemix.NymSigner{NymSignatureScheme: fakeSignatureScheme}
var err error
sk := &mock.Big{}
sk.BytesReturns([]byte{1, 2, 3, 4}, nil)
nymSK, err = idemix.NewNymSecretKey(sk, nil, false)
Expect(err).NotTo(HaveOccurred())
})
Context("and the underlying cryptographic algorithm succeed", func() {
var (
fakeSignature []byte
)
BeforeEach(func() {
fakeSignature = []byte("fake signature")
fakeSignatureScheme.SignReturns(fakeSignature, nil)
})
It("returns no error and a signature", func() {
signature, err := NymSigner.Sign(
idemix.NewUserSecretKey(nil, false),
[]byte("a digest"),
&bccsp.IdemixNymSignerOpts{
Nym: nymSK,
IssuerPK: idemix.NewIssuerPublicKey(nil),
},
)
Expect(err).NotTo(HaveOccurred())
Expect(signature).To(BeEquivalentTo(fakeSignature))
})
})
Context("and the underlying cryptographic algorithm fails", func() {
BeforeEach(func() {
fakeSignatureScheme.SignReturns(nil, errors.New("sign error"))
})
It("returns an error", func() {
signature, err := NymSigner.Sign(
idemix.NewUserSecretKey(nil, false),
[]byte("a digest"),
&bccsp.IdemixNymSignerOpts{
Nym: nymSK,
IssuerPK: idemix.NewIssuerPublicKey(nil),
},
)
Expect(err).To(MatchError("sign error"))
Expect(signature).To(BeNil())
})
})
Context("and the parameters are not well formed", func() {
Context("and the user secret key is nil", func() {
It("returns error", func() {
signature, err := NymSigner.Sign(
nil,
[]byte("a digest"),
&bccsp.IdemixNymSignerOpts{
Nym: nymSK,
IssuerPK: idemix.NewIssuerPublicKey(nil),
},
)
Expect(err).To(MatchError("invalid key, expected *userSecretKey"))
Expect(signature).To(BeNil())
})
})
Context("and the user secret key is not of type *userSecretKey", func() {
It("returns error", func() {
signature, err := NymSigner.Sign(
idemix.NewIssuerPublicKey(nil),
[]byte("a digest"),
&bccsp.IdemixNymSignerOpts{
Nym: nymSK,
IssuerPK: idemix.NewIssuerPublicKey(nil),
},
)
Expect(err).To(MatchError("invalid key, expected *userSecretKey"))
Expect(signature).To(BeNil())
})
})
Context("and the option is nil", func() {
It("returns error", func() {
signature, err := NymSigner.Sign(
idemix.NewUserSecretKey(nil, false),
[]byte("a digest"),
nil,
)
Expect(err).To(MatchError("invalid options, expected *IdemixNymSignerOpts"))
Expect(signature).To(BeNil())
})
})
Context("and the option is not of type *IdemixNymSignerOpts", func() {
It("returns error", func() {
signature, err := NymSigner.Sign(
idemix.NewUserSecretKey(nil, false),
[]byte("a digest"),
&bccsp.IdemixCRISignerOpts{},
)
Expect(err).To(MatchError("invalid options, expected *IdemixNymSignerOpts"))
Expect(signature).To(BeNil())
})
})
Context("and the nym is nil", func() {
It("returns error", func() {
signature, err := NymSigner.Sign(
idemix.NewUserSecretKey(nil, false),
[]byte("a digest"),
&bccsp.IdemixNymSignerOpts{
IssuerPK: idemix.NewIssuerPublicKey(nil),
},
)
Expect(err).To(MatchError("invalid options, missing nym key"))
Expect(signature).To(BeNil())
})
})
Context("and the nym is not of type *nymSecretKey", func() {
It("returns error", func() {
signature, err := NymSigner.Sign(
idemix.NewUserSecretKey(nil, false),
[]byte("a digest"),
&bccsp.IdemixNymSignerOpts{
Nym: idemix.NewIssuerPublicKey(nil),
IssuerPK: idemix.NewIssuerPublicKey(nil),
},
)
Expect(err).To(MatchError("invalid nym key, expected *nymSecretKey"))
Expect(signature).To(BeNil())
})
})
Context("and the IssuerPk is nil", func() {
It("returns error", func() {
signature, err := NymSigner.Sign(
idemix.NewUserSecretKey(nil, false),
[]byte("a digest"),
&bccsp.IdemixNymSignerOpts{
Nym: nymSK,
},
)
Expect(err).To(MatchError("invalid options, missing issuer public key"))
Expect(signature).To(BeNil())
})
})
Context("and the IssuerPk is not of type *issuerPublicKey", func() {
It("returns error", func() {
signature, err := NymSigner.Sign(
idemix.NewUserSecretKey(nil, false),
[]byte("a digest"),
&bccsp.IdemixNymSignerOpts{
Nym: nymSK,
IssuerPK: idemix.NewUserSecretKey(nil, false),
},
)
Expect(err).To(MatchError("invalid issuer public key, expected *issuerPublicKey"))
Expect(signature).To(BeNil())
})
})
})
})
Describe("when verifying a signature", func() {
var (
NymVerifier *idemix.NymVerifier
fakeSignatureScheme *mock.NymSignatureScheme
)
BeforeEach(func() {
fakeSignatureScheme = &mock.NymSignatureScheme{}
NymVerifier = &idemix.NymVerifier{NymSignatureScheme: fakeSignatureScheme}
})
Context("and the underlying cryptographic algorithm succeed", func() {
BeforeEach(func() {
fakeSignatureScheme.VerifyReturns(nil)
})
It("returns no error and valid signature", func() {
valid, err := NymVerifier.Verify(
idemix.NewNymPublicKey(nil),
[]byte("a signature"),
[]byte("a digest"),
&bccsp.IdemixNymSignerOpts{
IssuerPK: idemix.NewIssuerPublicKey(nil),
},
)
Expect(err).NotTo(HaveOccurred())
Expect(valid).To(BeTrue())
})
})
Context("and the underlying cryptographic algorithm fails", func() {
BeforeEach(func() {
fakeSignatureScheme.VerifyReturns(errors.New("verify error"))
})
It("returns an error", func() {
valid, err := NymVerifier.Verify(
idemix.NewNymPublicKey(nil),
[]byte("a signature"),
[]byte("a digest"),
&bccsp.IdemixNymSignerOpts{
IssuerPK: idemix.NewIssuerPublicKey(nil),
},
)
Expect(err).To(MatchError("verify error"))
Expect(valid).To(BeFalse())
})
})
Context("and the parameters are not well formed", func() {
Context("and the nym public key is nil", func() {
It("returns error", func() {
valid, err := NymVerifier.Verify(
nil,
[]byte("fake signature"),
nil,
&bccsp.IdemixNymSignerOpts{IssuerPK: idemix.NewIssuerPublicKey(nil)},
)
Expect(err).To(MatchError("invalid key, expected *nymPublicKey"))
Expect(valid).To(BeFalse())
})
})
Context("and the nym public key is not of type *nymPublicKey", func() {
It("returns error", func() {
valid, err := NymVerifier.Verify(
idemix.NewUserSecretKey(nil, false),
[]byte("fake signature"),
nil,
&bccsp.IdemixNymSignerOpts{IssuerPK: idemix.NewIssuerPublicKey(nil)},
)
Expect(err).To(MatchError("invalid key, expected *nymPublicKey"))
Expect(valid).To(BeFalse())
})
})
Context("and the signature is empty", func() {
It("returns error", func() {
valid, err := NymVerifier.Verify(