diff --git a/gossip/identity/identity.go b/gossip/identity/identity.go new file mode 100644 index 00000000000..28dc3ca28c0 --- /dev/null +++ b/gossip/identity/identity.go @@ -0,0 +1,116 @@ +/* +Copyright IBM Corp. 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package identity + +import ( + "bytes" + "fmt" + "sync" + + "github.com/hyperledger/fabric/gossip/api" + "github.com/hyperledger/fabric/gossip/common" +) + +// Mapper holds mappings between pkiID +// to certificates(identities) of peers +type Mapper interface { + // Put associates an identity to its given pkiID, and returns an error + // in case the given pkiID doesn't match the identity + Put(pkiID common.PKIidType, identity api.PeerIdentityType) error + + // Get returns the identity of a given pkiID, or error if such an identity + // isn't found + Get(pkiID common.PKIidType) (api.PeerIdentityType, error) + + // Sign signs a message, returns a signed message on success + // or an error on failure + Sign(msg []byte) ([]byte, error) + + // Verify verifies a signed message + Verify(vkID, signature, message []byte) error + + // GetPKIidOfCert returns the PKI-ID of a certificate + GetPKIidOfCert(api.PeerIdentityType) common.PKIidType +} + +// identityMapperImpl is a struct that implements Mapper +type identityMapperImpl struct { + mcs api.MessageCryptoService + pkiID2Cert map[string]api.PeerIdentityType + sync.RWMutex +} + +// NewIdentityStore method, all we need is a reference to a MessageCryptoService +func NewIdentityMapper(mcs api.MessageCryptoService) Mapper { + return &identityMapperImpl{ + mcs: mcs, + pkiID2Cert: make(map[string]api.PeerIdentityType), + } +} + +// put associates an identity to its given pkiID, and returns an error +// in case the given pkiID doesn't match the identity +func (is *identityMapperImpl) Put(pkiID common.PKIidType, identity api.PeerIdentityType) error { + if pkiID == nil { + return fmt.Errorf("pkiID is nil") + } + if identity == nil { + return fmt.Errorf("identity is nil") + } + + id := is.mcs.GetPKIidOfCert(identity) + if !bytes.Equal(pkiID, id) { + return fmt.Errorf("Identity doesn't match the computed pkiID") + } + + is.Lock() + defer is.Unlock() + is.pkiID2Cert[string(id)] = identity + return nil +} + +// get returns the identity of a given pkiID, or error if such an identity +// isn't found +func (is *identityMapperImpl) Get(pkiID common.PKIidType) (api.PeerIdentityType, error) { + is.RLock() + defer is.RUnlock() + identity, exists := is.pkiID2Cert[string(pkiID)] + if !exists { + return nil, fmt.Errorf("pkiID wasn't found") + } + return identity, nil +} + +// Sign signs a message, returns a signed message on success +// or an error on failure +func (is *identityMapperImpl) Sign(msg []byte) ([]byte, error) { + return is.mcs.Sign(msg) +} + +// Verify verifies a signed message +func (is *identityMapperImpl) Verify(vkID, signature, message []byte) error { + cert, err := is.Get(vkID) + if err != nil { + return err + } + return is.mcs.Verify(cert, signature, message) +} + +// GetPKIidOfCert returns the PKI-ID of a certificate +func (is *identityMapperImpl) GetPKIidOfCert(identity api.PeerIdentityType) common.PKIidType { + return is.mcs.GetPKIidOfCert(identity) +} diff --git a/gossip/identity/identity_test.go b/gossip/identity/identity_test.go new file mode 100644 index 00000000000..276fe509308 --- /dev/null +++ b/gossip/identity/identity_test.go @@ -0,0 +1,105 @@ +/* +Copyright IBM Corp. 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package identity + +import ( + "bytes" + "fmt" + "testing" + + "github.com/hyperledger/fabric/gossip/api" + "github.com/hyperledger/fabric/gossip/common" + "github.com/stretchr/testify/assert" +) + +var msgCryptoService = &naiveCryptoService{} + +type naiveCryptoService struct { +} + +func (*naiveCryptoService) ValidateIdentity(peerIdentity api.PeerIdentityType) error { + return nil +} + +// GetPKIidOfCert returns the PKI-ID of a peer's identity +func (*naiveCryptoService) GetPKIidOfCert(peerIdentity api.PeerIdentityType) common.PKIidType { + return common.PKIidType(peerIdentity) +} + +// VerifyBlock returns nil if the block is properly signed, +// else returns error +func (*naiveCryptoService) VerifyBlock(signedBlock api.SignedBlock) error { + return nil +} + +// Sign signs msg with this peer's signing key and outputs +// the signature if no error occurred. +func (*naiveCryptoService) Sign(msg []byte) ([]byte, error) { + return msg, nil +} + +// Verify checks that signature is a valid signature of message under a peer's verification key. +// If the verification succeeded, Verify returns nil meaning no error occurred. +// If peerCert is nil, then the signature is verified against this peer's verification key. +func (*naiveCryptoService) Verify(peerIdentity api.PeerIdentityType, signature, message []byte) error { + equal := bytes.Equal(signature, message) + if !equal { + return fmt.Errorf("Wrong certificate") + } + return nil +} + +func TestPut(t *testing.T) { + idStore := NewIdentityMapper(msgCryptoService) + identity := []byte("yacovm") + identity2 := []byte("not-yacovm") + pkiID := msgCryptoService.GetPKIidOfCert(api.PeerIdentityType(identity)) + pkiID2 := msgCryptoService.GetPKIidOfCert(api.PeerIdentityType(identity2)) + assert.NoError(t, idStore.Put(pkiID, identity)) + assert.Error(t, idStore.Put(nil, identity)) + assert.Error(t, idStore.Put(pkiID2, nil)) + assert.Error(t, idStore.Put(pkiID2, identity)) + assert.Error(t, idStore.Put(pkiID, identity2)) +} + +func TestGet(t *testing.T) { + idStore := NewIdentityMapper(msgCryptoService) + identity := []byte("yacovm") + identity2 := []byte("not-yacovm") + pkiID := msgCryptoService.GetPKIidOfCert(api.PeerIdentityType(identity)) + pkiID2 := msgCryptoService.GetPKIidOfCert(api.PeerIdentityType(identity2)) + assert.NoError(t, idStore.Put(pkiID, identity)) + cert, err := idStore.Get(pkiID) + assert.NoError(t, err) + assert.Equal(t, api.PeerIdentityType(identity), cert) + cert, err = idStore.Get(pkiID2) + assert.Nil(t, cert) + assert.Error(t, err) +} + +func TestVerify(t *testing.T) { + idStore := NewIdentityMapper(msgCryptoService) + identity := []byte("yacovm") + identity2 := []byte("not-yacovm") + pkiID := msgCryptoService.GetPKIidOfCert(api.PeerIdentityType(identity)) + pkiID2 := msgCryptoService.GetPKIidOfCert(api.PeerIdentityType(identity2)) + idStore.Put(pkiID, api.PeerIdentityType(identity)) + signed, err := idStore.Sign([]byte("bla bla")) + assert.NoError(t, err) + assert.NoError(t, idStore.Verify(pkiID, signed, []byte("bla bla"))) + assert.Error(t, idStore.Verify(pkiID2, signed, []byte("bla bla"))) +}