1610 lines
48 KiB
Go
1610 lines
48 KiB
Go
/*
|
|
Copyright IBM Corp. 2017 All Rights Reserved.
|
|
|
|
SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
package msp
|
|
|
|
import (
|
|
"crypto/ecdsa"
|
|
"crypto/elliptic"
|
|
"crypto/rand"
|
|
"crypto/sha256"
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"encoding/asn1"
|
|
"encoding/hex"
|
|
"encoding/pem"
|
|
"errors"
|
|
"fmt"
|
|
"math/big"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/golang/protobuf/proto"
|
|
"github.com/hyperledger/fabric-protos-go/msp"
|
|
"github.com/hyperledger/fabric/bccsp"
|
|
"github.com/hyperledger/fabric/bccsp/factory"
|
|
"github.com/hyperledger/fabric/bccsp/sw"
|
|
"github.com/hyperledger/fabric/bccsp/utils"
|
|
"github.com/hyperledger/fabric/core/config/configtest"
|
|
"github.com/hyperledger/fabric/protoutil"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
var notACert = `-----BEGIN X509 CRL-----
|
|
MIIBYzCCAQgCAQEwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMCVVMxEzARBgNVBAgT
|
|
CkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xHzAdBgNVBAoTFklu
|
|
dGVybmV0IFdpZGdldHMsIEluYy4xDDAKBgNVBAsTA1dXVzEUMBIGA1UEAxMLZXhh
|
|
bXBsZS5jb20XDTE3MDEyMzIwNTYyMFoXDTE3MDEyNjIwNTYyMFowJzAlAhQERXCx
|
|
LHROap1vM3CV40EHOghPTBcNMTcwMTIzMjA0NzMxWqAvMC0wHwYDVR0jBBgwFoAU
|
|
F2dCPaqegj/ExR2fW8OZ0bWcSBAwCgYDVR0UBAMCAQgwCgYIKoZIzj0EAwIDSQAw
|
|
RgIhAOTTpQYkGO+gwVe1LQOcNMD5fzFViOwBUraMrk6dRMlmAiEA8z2dpXKGwHrj
|
|
FRBbKkDnSpaVcZgjns+mLdHV2JkF0gk=
|
|
-----END X509 CRL-----`
|
|
|
|
func TestMSPParsers(t *testing.T) {
|
|
_, _, err := localMsp.(*bccspmsp).getIdentityFromConf(nil)
|
|
require.Error(t, err)
|
|
_, _, err = localMsp.(*bccspmsp).getIdentityFromConf([]byte("barf"))
|
|
require.Error(t, err)
|
|
_, _, err = localMsp.(*bccspmsp).getIdentityFromConf([]byte(notACert))
|
|
require.Error(t, err)
|
|
|
|
_, err = localMsp.(*bccspmsp).getSigningIdentityFromConf(nil)
|
|
require.Error(t, err)
|
|
|
|
sigid := &msp.SigningIdentityInfo{PublicSigner: []byte("barf"), PrivateSigner: nil}
|
|
_, err = localMsp.(*bccspmsp).getSigningIdentityFromConf(sigid)
|
|
require.Error(t, err)
|
|
|
|
keyinfo := &msp.KeyInfo{KeyIdentifier: "PEER", KeyMaterial: nil}
|
|
sigid = &msp.SigningIdentityInfo{PublicSigner: []byte("barf"), PrivateSigner: keyinfo}
|
|
_, err = localMsp.(*bccspmsp).getSigningIdentityFromConf(sigid)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestGetSigningIdentityFromConfWithWrongPrivateCert(t *testing.T) {
|
|
// Temporary Replace root certs
|
|
oldRoots := localMsp.(*bccspmsp).opts.Roots
|
|
defer func() {
|
|
// Restore original root certs
|
|
localMsp.(*bccspmsp).opts.Roots = oldRoots
|
|
}()
|
|
_, cert := generateSelfSignedCert(t, time.Now())
|
|
localMsp.(*bccspmsp).opts.Roots = x509.NewCertPool()
|
|
localMsp.(*bccspmsp).opts.Roots.AddCert(cert)
|
|
|
|
// Use self signed cert as public key. Convert DER to PEM format
|
|
pem := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw})
|
|
|
|
// Use wrong formatted private cert
|
|
keyinfo := &msp.KeyInfo{
|
|
KeyMaterial: []byte("wrong encoding"),
|
|
KeyIdentifier: "MyPrivateKey",
|
|
}
|
|
sigid := &msp.SigningIdentityInfo{PublicSigner: pem, PrivateSigner: keyinfo}
|
|
_, err := localMsp.(*bccspmsp).getSigningIdentityFromConf(sigid)
|
|
require.EqualError(t, err, "MyPrivateKey: wrong PEM encoding")
|
|
}
|
|
|
|
func TestMSPSetupNoCryptoConf(t *testing.T) {
|
|
mspDir := configtest.GetDevMspDir()
|
|
conf, err := GetLocalMspConfig(mspDir, nil, "SampleOrg")
|
|
if err != nil {
|
|
fmt.Printf("Setup should have succeeded, got err %s instead", err)
|
|
os.Exit(-1)
|
|
}
|
|
|
|
mspconf := &msp.FabricMSPConfig{}
|
|
err = proto.Unmarshal(conf.Config, mspconf)
|
|
require.NoError(t, err)
|
|
|
|
// here we test the case of an MSP configuration
|
|
// where the hash function to be used to obtain
|
|
// the identity identifier is unspecified - a
|
|
// sane default should be picked
|
|
mspconf.CryptoConfig.IdentityIdentifierHashFunction = ""
|
|
b, err := proto.Marshal(mspconf)
|
|
require.NoError(t, err)
|
|
conf.Config = b
|
|
newmsp, err := newBccspMsp(MSPv1_0, factory.GetDefault())
|
|
require.NoError(t, err)
|
|
err = newmsp.Setup(conf)
|
|
require.NoError(t, err)
|
|
|
|
// here we test the case of an MSP configuration
|
|
// where the hash function to be used to compute
|
|
// signatures is unspecified - a sane default
|
|
// should be picked
|
|
mspconf.CryptoConfig.SignatureHashFamily = ""
|
|
b, err = proto.Marshal(mspconf)
|
|
require.NoError(t, err)
|
|
conf.Config = b
|
|
newmsp, err = newBccspMsp(MSPv1_0, factory.GetDefault())
|
|
require.NoError(t, err)
|
|
err = newmsp.Setup(conf)
|
|
require.NoError(t, err)
|
|
|
|
// here we test the case of an MSP configuration
|
|
// that has NO crypto configuration specified;
|
|
// the code will use appropriate defaults
|
|
mspconf.CryptoConfig = nil
|
|
b, err = proto.Marshal(mspconf)
|
|
require.NoError(t, err)
|
|
conf.Config = b
|
|
newmsp, err = newBccspMsp(MSPv1_0, factory.GetDefault())
|
|
require.NoError(t, err)
|
|
err = newmsp.Setup(conf)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestGetters(t *testing.T) {
|
|
typ := localMsp.GetType()
|
|
require.Equal(t, typ, FABRIC)
|
|
require.NotNil(t, localMsp.GetTLSRootCerts())
|
|
require.NotNil(t, localMsp.GetTLSIntermediateCerts())
|
|
}
|
|
|
|
func TestMSPSetupBad(t *testing.T) {
|
|
_, err := GetLocalMspConfig("barf", nil, "SampleOrg")
|
|
if err == nil {
|
|
t.Fatalf("Setup should have failed on an invalid config file")
|
|
return
|
|
}
|
|
|
|
mgr := NewMSPManager()
|
|
err = mgr.Setup(nil)
|
|
require.NoError(t, err)
|
|
err = mgr.Setup([]MSP{})
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestDoubleSetup(t *testing.T) {
|
|
// note that we've already called setup once on this
|
|
err := mspMgr.Setup(nil)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
type bccspNoKeyLookupKS struct {
|
|
bccsp.BCCSP
|
|
}
|
|
|
|
func (*bccspNoKeyLookupKS) GetKey(ski []byte) (k bccsp.Key, err error) {
|
|
return nil, errors.New("not found")
|
|
}
|
|
|
|
func TestNotFoundInBCCSP(t *testing.T) {
|
|
cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
|
|
require.NoError(t, err)
|
|
|
|
dir := configtest.GetDevMspDir()
|
|
conf, err := GetLocalMspConfig(dir, nil, "SampleOrg")
|
|
|
|
require.NoError(t, err)
|
|
|
|
thisMSP, err := newBccspMsp(MSPv1_0, cryptoProvider)
|
|
require.NoError(t, err)
|
|
ks, err := sw.NewFileBasedKeyStore(nil, filepath.Join(dir, "keystore"), true)
|
|
require.NoError(t, err)
|
|
csp, err := sw.NewWithParams(256, "SHA2", ks)
|
|
require.NoError(t, err)
|
|
thisMSP.(*bccspmsp).bccsp = &bccspNoKeyLookupKS{csp}
|
|
|
|
err = thisMSP.Setup(conf)
|
|
require.Error(t, err)
|
|
require.Contains(t, "KeyMaterial not found in SigningIdentityInfo", err.Error())
|
|
}
|
|
|
|
func TestGetIdentities(t *testing.T) {
|
|
_, err := localMsp.GetDefaultSigningIdentity()
|
|
if err != nil {
|
|
t.Fatalf("GetDefaultSigningIdentity failed with err %s", err)
|
|
return
|
|
}
|
|
}
|
|
|
|
func TestDeserializeIdentityFails(t *testing.T) {
|
|
_, err := localMsp.DeserializeIdentity([]byte("barf"))
|
|
require.Error(t, err)
|
|
|
|
id := &msp.SerializedIdentity{Mspid: "SampleOrg", IdBytes: []byte("barfr")}
|
|
b, err := proto.Marshal(id)
|
|
require.NoError(t, err)
|
|
_, err = localMsp.DeserializeIdentity(b)
|
|
require.Error(t, err)
|
|
|
|
id = &msp.SerializedIdentity{Mspid: "SampleOrg", IdBytes: []byte(notACert)}
|
|
b, err = proto.Marshal(id)
|
|
require.NoError(t, err)
|
|
_, err = localMsp.DeserializeIdentity(b)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestGetSigningIdentityFromVerifyingMSP(t *testing.T) {
|
|
cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
|
|
require.NoError(t, err)
|
|
|
|
mspDir := configtest.GetDevMspDir()
|
|
conf, err = GetVerifyingMspConfig(mspDir, "SampleOrg", ProviderTypeToString(FABRIC))
|
|
if err != nil {
|
|
fmt.Printf("Setup should have succeeded, got err %s instead", err)
|
|
os.Exit(-1)
|
|
}
|
|
|
|
newmsp, err := newBccspMsp(MSPv1_0, cryptoProvider)
|
|
require.NoError(t, err)
|
|
err = newmsp.Setup(conf)
|
|
require.NoError(t, err)
|
|
|
|
_, err = newmsp.GetDefaultSigningIdentity()
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestValidateDefaultSigningIdentity(t *testing.T) {
|
|
id, err := localMsp.GetDefaultSigningIdentity()
|
|
require.NoError(t, err)
|
|
|
|
err = localMsp.Validate(id.GetPublicVersion())
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestSerializeIdentities(t *testing.T) {
|
|
id, err := localMsp.GetDefaultSigningIdentity()
|
|
if err != nil {
|
|
t.Fatalf("GetDefaultSigningIdentity should have succeeded, got err %s", err)
|
|
return
|
|
}
|
|
|
|
serializedID, err := id.Serialize()
|
|
if err != nil {
|
|
t.Fatalf("Serialize should have succeeded, got err %s", err)
|
|
return
|
|
}
|
|
|
|
idBack, err := localMsp.DeserializeIdentity(serializedID)
|
|
if err != nil {
|
|
t.Fatalf("DeserializeIdentity should have succeeded, got err %s", err)
|
|
return
|
|
}
|
|
|
|
err = localMsp.Validate(idBack)
|
|
if err != nil {
|
|
t.Fatalf("The identity should be valid, got err %s", err)
|
|
return
|
|
}
|
|
|
|
if !reflect.DeepEqual(id.GetPublicVersion(), idBack) {
|
|
t.Fatalf("Identities should be equal (%s) (%s)", id, idBack)
|
|
return
|
|
}
|
|
}
|
|
|
|
func computeSKI(key *ecdsa.PublicKey) []byte {
|
|
raw := elliptic.Marshal(key.Curve, key.X, key.Y)
|
|
hash := sha256.Sum256(raw)
|
|
return hash[:]
|
|
}
|
|
|
|
func TestValidHostname(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
valid bool
|
|
}{
|
|
{"", false},
|
|
{".", false},
|
|
{"example.com", true},
|
|
{"example.com.", true},
|
|
{"*.example.com", true},
|
|
{".example.com", false},
|
|
{"host.*.example.com", false},
|
|
{"localhost", true},
|
|
{"-localhost", false},
|
|
{"Not_Quite.example.com", true},
|
|
{"weird:colon.example.com", true},
|
|
{"1-2-3.example.com", true},
|
|
}
|
|
for _, tt := range tests {
|
|
if tt.valid {
|
|
require.True(t, validHostname(tt.name), "expected %s to be a valid hostname", tt.name)
|
|
} else {
|
|
require.False(t, validHostname(tt.name), "expected %s to be an invalid hostname", tt.name)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestValidateCANameConstraintsMitigation(t *testing.T) {
|
|
// Prior to Go 1.15, if a signing certificate contains a name constraint, the
|
|
// leaf certificate does not include a SAN, and the leaf common name looks
|
|
// like a valid hostname, the certificate chain would fail to validate.
|
|
// (This behavior may have been introduced with Go 1.10.)
|
|
//
|
|
// In Go 1.15, the behavior has changed and, by default, the same structure
|
|
// will validate. This test asserts on the old behavior.
|
|
caKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
require.NoError(t, err)
|
|
leafKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
require.NoError(t, err)
|
|
|
|
caKeyUsage := x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageCertSign | x509.KeyUsageCRLSign
|
|
caTemplate := x509.Certificate{
|
|
Subject: pkix.Name{CommonName: "TestCA"},
|
|
SerialNumber: big.NewInt(1),
|
|
NotBefore: time.Now().Add(-1 * time.Hour),
|
|
NotAfter: time.Now().Add(2 * time.Hour),
|
|
ExcludedDNSDomains: []string{"example.com"},
|
|
PermittedDNSDomainsCritical: true,
|
|
IsCA: true,
|
|
BasicConstraintsValid: true,
|
|
KeyUsage: caKeyUsage,
|
|
SubjectKeyId: computeSKI(caKey.Public().(*ecdsa.PublicKey)),
|
|
}
|
|
caCertBytes, err := x509.CreateCertificate(rand.Reader, &caTemplate, &caTemplate, caKey.Public(), caKey)
|
|
require.NoError(t, err)
|
|
ca, err := x509.ParseCertificate(caCertBytes)
|
|
require.NoError(t, err)
|
|
|
|
leafTemplate := x509.Certificate{
|
|
Subject: pkix.Name{CommonName: "localhost"},
|
|
SerialNumber: big.NewInt(2),
|
|
NotBefore: time.Now().Add(-1 * time.Hour),
|
|
NotAfter: time.Now().Add(2 * time.Hour),
|
|
KeyUsage: x509.KeyUsageDigitalSignature,
|
|
SubjectKeyId: computeSKI(leafKey.Public().(*ecdsa.PublicKey)),
|
|
}
|
|
leafCertBytes, err := x509.CreateCertificate(rand.Reader, &leafTemplate, ca, leafKey.Public(), caKey)
|
|
require.NoError(t, err)
|
|
|
|
keyBytes, err := x509.MarshalPKCS8PrivateKey(leafKey)
|
|
require.NoError(t, err)
|
|
|
|
caCertPem := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: caCertBytes})
|
|
leafCertPem := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: leafCertBytes})
|
|
leafKeyPem := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: keyBytes})
|
|
|
|
t.Run("VerifyNameConstraintsSingleCert", func(t *testing.T) {
|
|
for _, der := range [][]byte{caCertBytes, leafCertBytes} {
|
|
cert, err := x509.ParseCertificate(der)
|
|
require.NoError(t, err, "failed to parse certificate")
|
|
|
|
err = verifyLegacyNameConstraints([]*x509.Certificate{cert})
|
|
require.NoError(t, err, "single certificate should not trigger legacy constraints")
|
|
}
|
|
})
|
|
|
|
t.Run("VerifyNameConstraints", func(t *testing.T) {
|
|
var certs []*x509.Certificate
|
|
for _, der := range [][]byte{leafCertBytes, caCertBytes} {
|
|
cert, err := x509.ParseCertificate(der)
|
|
require.NoError(t, err, "failed to parse certificate")
|
|
certs = append(certs, cert)
|
|
}
|
|
|
|
err = verifyLegacyNameConstraints(certs)
|
|
require.Error(t, err, "certificate chain should trigger legacy constraints")
|
|
var cie x509.CertificateInvalidError
|
|
require.True(t, errors.As(err, &cie))
|
|
require.Equal(t, x509.NameConstraintsWithoutSANs, cie.Reason)
|
|
})
|
|
|
|
t.Run("VerifyNameConstraintsWithSAN", func(t *testing.T) {
|
|
caCert, err := x509.ParseCertificate(caCertBytes)
|
|
require.NoError(t, err)
|
|
|
|
leafTemplate := leafTemplate
|
|
leafTemplate.DNSNames = []string{"localhost"}
|
|
|
|
leafCertBytes, err := x509.CreateCertificate(rand.Reader, &leafTemplate, caCert, leafKey.Public(), caKey)
|
|
require.NoError(t, err)
|
|
|
|
leafCert, err := x509.ParseCertificate(leafCertBytes)
|
|
require.NoError(t, err)
|
|
|
|
err = verifyLegacyNameConstraints([]*x509.Certificate{leafCert, caCert})
|
|
require.NoError(t, err, "signer with name constraints and leaf with SANs should be valid")
|
|
})
|
|
|
|
t.Run("ValidationAtSetup", func(t *testing.T) {
|
|
fabricMSPConfig := &msp.FabricMSPConfig{
|
|
Name: "ConstraintsMSP",
|
|
RootCerts: [][]byte{caCertPem},
|
|
SigningIdentity: &msp.SigningIdentityInfo{
|
|
PublicSigner: leafCertPem,
|
|
PrivateSigner: &msp.KeyInfo{
|
|
KeyIdentifier: "Certificate Without SAN",
|
|
KeyMaterial: leafKeyPem,
|
|
},
|
|
},
|
|
}
|
|
mspConfig := &msp.MSPConfig{
|
|
Config: protoutil.MarshalOrPanic(fabricMSPConfig),
|
|
}
|
|
|
|
ks, err := sw.NewFileBasedKeyStore(nil, filepath.Join(configtest.GetDevMspDir(), "keystore"), true)
|
|
require.NoError(t, err)
|
|
cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(ks)
|
|
require.NoError(t, err)
|
|
|
|
testMSP, err := NewBccspMspWithKeyStore(MSPv1_0, ks, cryptoProvider)
|
|
require.NoError(t, err)
|
|
|
|
err = testMSP.Setup(mspConfig)
|
|
require.Error(t, err)
|
|
var cie x509.CertificateInvalidError
|
|
require.True(t, errors.As(err, &cie))
|
|
require.Equal(t, x509.NameConstraintsWithoutSANs, cie.Reason)
|
|
})
|
|
}
|
|
|
|
func TestIsWellFormed(t *testing.T) {
|
|
mspMgr := NewMSPManager()
|
|
|
|
id, err := localMsp.GetDefaultSigningIdentity()
|
|
if err != nil {
|
|
t.Fatalf("GetDefaultSigningIdentity should have succeeded, got err %s", err)
|
|
return
|
|
}
|
|
|
|
serializedID, err := id.Serialize()
|
|
if err != nil {
|
|
t.Fatalf("Serialize should have succeeded, got err %s", err)
|
|
return
|
|
}
|
|
|
|
sId := &msp.SerializedIdentity{}
|
|
err = proto.Unmarshal(serializedID, sId)
|
|
require.NoError(t, err)
|
|
|
|
// An MSP Manager without any MSPs should not recognize the identity since
|
|
// not providers are registered
|
|
err = mspMgr.IsWellFormed(sId)
|
|
require.Error(t, err)
|
|
require.Equal(t, "no MSP provider recognizes the identity", err.Error())
|
|
|
|
// Add the MSP to the MSP Manager
|
|
mspMgr.Setup([]MSP{localMsp})
|
|
|
|
err = localMsp.IsWellFormed(sId)
|
|
require.NoError(t, err)
|
|
err = mspMgr.IsWellFormed(sId)
|
|
require.NoError(t, err)
|
|
|
|
bl, _ := pem.Decode(sId.IdBytes)
|
|
require.Equal(t, "CERTIFICATE", bl.Type)
|
|
|
|
// Now, strip off the type from the PEM block. It should still be valid
|
|
bl.Type = ""
|
|
sId.IdBytes = pem.EncodeToMemory(bl)
|
|
|
|
err = localMsp.IsWellFormed(sId)
|
|
require.NoError(t, err)
|
|
|
|
// Now, corrupt the type of the PEM block.
|
|
// make sure it isn't considered well formed by both an MSP and an MSP Manager
|
|
bl.Type = "foo"
|
|
sId.IdBytes = pem.EncodeToMemory(bl)
|
|
err = localMsp.IsWellFormed(sId)
|
|
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "pem type is")
|
|
require.Contains(t, err.Error(), "should be 'CERTIFICATE' or missing")
|
|
|
|
err = mspMgr.IsWellFormed(sId)
|
|
require.Error(t, err)
|
|
require.Equal(t, "no MSP provider recognizes the identity", err.Error())
|
|
|
|
// Restore the identity to what it was
|
|
sId = &msp.SerializedIdentity{}
|
|
err = proto.Unmarshal(serializedID, sId)
|
|
require.NoError(t, err)
|
|
|
|
// Append some trailing junk at the end
|
|
sId.IdBytes = append(sId.IdBytes, []byte{1, 2, 3}...)
|
|
// And ensure it is deemed invalid
|
|
err = localMsp.IsWellFormed(sId)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "for MSP SampleOrg has trailing bytes")
|
|
|
|
// Parse the certificate of the identity
|
|
cert, err := x509.ParseCertificate(bl.Bytes)
|
|
require.NoError(t, err)
|
|
// Obtain the ECDSA signature
|
|
r, s, err := utils.UnmarshalECDSASignature(cert.Signature)
|
|
require.NoError(t, err)
|
|
|
|
// Modify it by appending some bytes to its end.
|
|
modifiedSig, err := asn1.Marshal(rst{
|
|
R: r,
|
|
S: s,
|
|
T: big.NewInt(100),
|
|
})
|
|
require.NoError(t, err)
|
|
newCert, err := certFromX509Cert(cert)
|
|
require.NoError(t, err)
|
|
newCert.SignatureValue.Bytes = modifiedSig
|
|
newCert.SignatureValue.BitLength = len(newCert.SignatureValue.Bytes) * 8
|
|
newCert.Raw = nil
|
|
// Pour it back into the identity
|
|
rawCert, err := asn1.Marshal(newCert)
|
|
require.NoError(t, err)
|
|
sId.IdBytes = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: rawCert})
|
|
// Ensure it is invalid now and the signature modification is detected
|
|
err = localMsp.IsWellFormed(sId)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "for MSP SampleOrg has a non canonical signature")
|
|
}
|
|
|
|
type rst struct {
|
|
R, S, T *big.Int
|
|
}
|
|
|
|
func TestValidateCAIdentity(t *testing.T) {
|
|
caID := getIdentity(t, cacerts)
|
|
|
|
err := localMsp.Validate(caID)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestBadAdminIdentity(t *testing.T) {
|
|
cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
|
|
require.NoError(t, err)
|
|
|
|
conf, err := GetLocalMspConfig("testdata/badadmin", nil, "SampleOrg")
|
|
require.NoError(t, err)
|
|
|
|
thisMSP, err := newBccspMsp(MSPv1_0, cryptoProvider)
|
|
require.NoError(t, err)
|
|
ks, err := sw.NewFileBasedKeyStore(nil, filepath.Join("testdata/badadmin", "keystore"), true)
|
|
require.NoError(t, err)
|
|
csp, err := sw.NewWithParams(256, "SHA2", ks)
|
|
require.NoError(t, err)
|
|
thisMSP.(*bccspmsp).bccsp = csp
|
|
|
|
err = thisMSP.Setup(conf)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestValidateAdminIdentity(t *testing.T) {
|
|
caID := getIdentity(t, admincerts)
|
|
|
|
err := localMsp.Validate(caID)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestSerializeIdentitiesWithWrongMSP(t *testing.T) {
|
|
id, err := localMsp.GetDefaultSigningIdentity()
|
|
if err != nil {
|
|
t.Fatalf("GetDefaultSigningIdentity should have succeeded, got err %s", err)
|
|
return
|
|
}
|
|
|
|
serializedID, err := id.Serialize()
|
|
if err != nil {
|
|
t.Fatalf("Serialize should have succeeded, got err %s", err)
|
|
return
|
|
}
|
|
|
|
sid := &msp.SerializedIdentity{}
|
|
err = proto.Unmarshal(serializedID, sid)
|
|
require.NoError(t, err)
|
|
|
|
sid.Mspid += "BARF"
|
|
|
|
serializedID, err = proto.Marshal(sid)
|
|
require.NoError(t, err)
|
|
|
|
_, err = localMsp.DeserializeIdentity(serializedID)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestSerializeIdentitiesWithMSPManager(t *testing.T) {
|
|
id, err := localMsp.GetDefaultSigningIdentity()
|
|
if err != nil {
|
|
t.Fatalf("GetDefaultSigningIdentity should have succeeded, got err %s", err)
|
|
return
|
|
}
|
|
|
|
serializedID, err := id.Serialize()
|
|
if err != nil {
|
|
t.Fatalf("Serialize should have succeeded, got err %s", err)
|
|
return
|
|
}
|
|
|
|
_, err = mspMgr.DeserializeIdentity(serializedID)
|
|
require.NoError(t, err)
|
|
|
|
sid := &msp.SerializedIdentity{}
|
|
err = proto.Unmarshal(serializedID, sid)
|
|
require.NoError(t, err)
|
|
|
|
sid.Mspid += "BARF"
|
|
|
|
serializedID, err = proto.Marshal(sid)
|
|
require.NoError(t, err)
|
|
|
|
_, err = mspMgr.DeserializeIdentity(serializedID)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), fmt.Sprintf("MSP %s is not defined on channel", sid.Mspid))
|
|
|
|
_, err = mspMgr.DeserializeIdentity([]byte("barf"))
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "could not deserialize")
|
|
}
|
|
|
|
func TestIdentitiesGetters(t *testing.T) {
|
|
id, err := localMsp.GetDefaultSigningIdentity()
|
|
if err != nil {
|
|
t.Fatalf("GetDefaultSigningIdentity should have succeeded, got err %s", err)
|
|
return
|
|
}
|
|
|
|
idid := id.GetIdentifier()
|
|
require.NotNil(t, idid)
|
|
mspid := id.GetMSPIdentifier()
|
|
require.NotNil(t, mspid)
|
|
require.False(t, id.Anonymous())
|
|
}
|
|
|
|
func TestSignAndVerify(t *testing.T) {
|
|
id, err := localMsp.GetDefaultSigningIdentity()
|
|
if err != nil {
|
|
t.Fatalf("GetDefaultSigningIdentity should have succeeded")
|
|
return
|
|
}
|
|
|
|
serializedID, err := id.Serialize()
|
|
if err != nil {
|
|
t.Fatalf("Serialize should have succeeded")
|
|
return
|
|
}
|
|
|
|
idBack, err := localMsp.DeserializeIdentity(serializedID)
|
|
if err != nil {
|
|
t.Fatalf("DeserializeIdentity should have succeeded")
|
|
return
|
|
}
|
|
|
|
msg := []byte("foo")
|
|
sig, err := id.Sign(msg)
|
|
if err != nil {
|
|
t.Fatalf("Sign should have succeeded")
|
|
return
|
|
}
|
|
|
|
err = id.Verify(msg, sig)
|
|
if err != nil {
|
|
t.Fatalf("The signature should be valid")
|
|
return
|
|
}
|
|
|
|
err = idBack.Verify(msg, sig)
|
|
if err != nil {
|
|
t.Fatalf("The signature should be valid")
|
|
return
|
|
}
|
|
|
|
err = id.Verify(msg[1:], sig)
|
|
require.Error(t, err)
|
|
err = id.Verify(msg, sig[1:])
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestSignAndVerifyFailures(t *testing.T) {
|
|
msg := []byte("foo")
|
|
|
|
id, err := localMspBad.GetDefaultSigningIdentity()
|
|
if err != nil {
|
|
t.Fatalf("GetDefaultSigningIdentity should have succeeded")
|
|
return
|
|
}
|
|
|
|
hash := id.(*signingidentity).msp.cryptoConfig.SignatureHashFamily
|
|
id.(*signingidentity).msp.cryptoConfig.SignatureHashFamily = "barf"
|
|
|
|
_, err = id.Sign(msg)
|
|
require.Error(t, err)
|
|
|
|
id.(*signingidentity).msp.cryptoConfig.SignatureHashFamily = hash
|
|
|
|
sig, err := id.Sign(msg)
|
|
if err != nil {
|
|
t.Fatalf("Sign should have succeeded")
|
|
return
|
|
}
|
|
|
|
id.(*signingidentity).msp.cryptoConfig.SignatureHashFamily = "barf"
|
|
|
|
err = id.Verify(msg, sig)
|
|
require.Error(t, err)
|
|
|
|
id.(*signingidentity).msp.cryptoConfig.SignatureHashFamily = hash
|
|
}
|
|
|
|
func TestSignAndVerifyOtherHash(t *testing.T) {
|
|
id, err := localMsp.GetDefaultSigningIdentity()
|
|
if err != nil {
|
|
t.Fatalf("GetDefaultSigningIdentity should have succeeded")
|
|
return
|
|
}
|
|
|
|
hash := id.(*signingidentity).msp.cryptoConfig.SignatureHashFamily
|
|
id.(*signingidentity).msp.cryptoConfig.SignatureHashFamily = bccsp.SHA3
|
|
|
|
msg := []byte("foo")
|
|
sig, err := id.Sign(msg)
|
|
if err != nil {
|
|
t.Fatalf("Sign should have succeeded")
|
|
return
|
|
}
|
|
|
|
err = id.Verify(msg, sig)
|
|
require.NoError(t, err)
|
|
|
|
id.(*signingidentity).msp.cryptoConfig.SignatureHashFamily = hash
|
|
}
|
|
|
|
func TestSignAndVerify_longMessage(t *testing.T) {
|
|
id, err := localMsp.GetDefaultSigningIdentity()
|
|
if err != nil {
|
|
t.Fatalf("GetDefaultSigningIdentity should have succeeded")
|
|
return
|
|
}
|
|
|
|
serializedID, err := id.Serialize()
|
|
if err != nil {
|
|
t.Fatalf("Serialize should have succeeded")
|
|
return
|
|
}
|
|
|
|
idBack, err := localMsp.DeserializeIdentity(serializedID)
|
|
if err != nil {
|
|
t.Fatalf("DeserializeIdentity should have succeeded")
|
|
return
|
|
}
|
|
|
|
msg := []byte("ABCDEFGABCDEFGABCDEFGABCDEFGABCDEFGABCDEFGABCDEFGABCDEFGABCDEFGABCDEFGABCDEFGABCDEFGABCDEFGABCDEFG")
|
|
sig, err := id.Sign(msg)
|
|
if err != nil {
|
|
t.Fatalf("Sign should have succeeded")
|
|
return
|
|
}
|
|
|
|
err = id.Verify(msg, sig)
|
|
if err != nil {
|
|
t.Fatalf("The signature should be valid")
|
|
return
|
|
}
|
|
|
|
err = idBack.Verify(msg, sig)
|
|
if err != nil {
|
|
t.Fatalf("The signature should be valid")
|
|
return
|
|
}
|
|
}
|
|
|
|
func TestGetOU(t *testing.T) {
|
|
id, err := localMsp.GetDefaultSigningIdentity()
|
|
if err != nil {
|
|
t.Fatalf("GetDefaultSigningIdentity should have succeeded")
|
|
return
|
|
}
|
|
|
|
require.Equal(t, "COP", id.GetOrganizationalUnits()[0].OrganizationalUnitIdentifier)
|
|
}
|
|
|
|
func TestGetOUFail(t *testing.T) {
|
|
id, err := localMspBad.GetDefaultSigningIdentity()
|
|
if err != nil {
|
|
t.Fatalf("GetDefaultSigningIdentity should have succeeded")
|
|
return
|
|
}
|
|
|
|
certTmp := id.(*signingidentity).cert
|
|
id.(*signingidentity).cert = nil
|
|
ou := id.GetOrganizationalUnits()
|
|
require.Nil(t, ou)
|
|
|
|
id.(*signingidentity).cert = certTmp
|
|
|
|
opts := id.(*signingidentity).msp.opts
|
|
id.(*signingidentity).msp.opts = nil
|
|
ou = id.GetOrganizationalUnits()
|
|
require.Nil(t, ou)
|
|
|
|
id.(*signingidentity).msp.opts = opts
|
|
}
|
|
|
|
func TestCertificationIdentifierComputation(t *testing.T) {
|
|
id, err := localMsp.GetDefaultSigningIdentity()
|
|
require.NoError(t, err)
|
|
|
|
chain, err := localMsp.(*bccspmsp).getCertificationChain(id.GetPublicVersion())
|
|
require.NoError(t, err)
|
|
|
|
// Hash the chain
|
|
// Use the hash of the identity's certificate as id in the IdentityIdentifier
|
|
hashOpt, err := bccsp.GetHashOpt(localMsp.(*bccspmsp).cryptoConfig.IdentityIdentifierHashFunction)
|
|
require.NoError(t, err)
|
|
|
|
hf, err := localMsp.(*bccspmsp).bccsp.GetHash(hashOpt)
|
|
require.NoError(t, err)
|
|
// Skipping first cert because it belongs to the identity
|
|
for i := 1; i < len(chain); i++ {
|
|
hf.Write(chain[i].Raw)
|
|
}
|
|
sum := hf.Sum(nil)
|
|
|
|
require.Equal(t, sum, id.GetOrganizationalUnits()[0].CertifiersIdentifier)
|
|
}
|
|
|
|
func TestOUPolicyPrincipal(t *testing.T) {
|
|
id, err := localMsp.GetDefaultSigningIdentity()
|
|
require.NoError(t, err)
|
|
|
|
cid, err := localMsp.(*bccspmsp).getCertificationChainIdentifier(id.GetPublicVersion())
|
|
require.NoError(t, err)
|
|
|
|
ou := &msp.OrganizationUnit{
|
|
OrganizationalUnitIdentifier: "COP",
|
|
MspIdentifier: "SampleOrg",
|
|
CertifiersIdentifier: cid,
|
|
}
|
|
bytes, err := proto.Marshal(ou)
|
|
require.NoError(t, err)
|
|
|
|
principal := &msp.MSPPrincipal{
|
|
PrincipalClassification: msp.MSPPrincipal_ORGANIZATION_UNIT,
|
|
Principal: bytes,
|
|
}
|
|
|
|
err = id.SatisfiesPrincipal(principal)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestOUPolicyPrincipalBadPrincipal(t *testing.T) {
|
|
id, err := localMsp.GetDefaultSigningIdentity()
|
|
require.NoError(t, err)
|
|
|
|
principal := &msp.MSPPrincipal{
|
|
PrincipalClassification: msp.MSPPrincipal_ORGANIZATION_UNIT,
|
|
Principal: []byte("barf"),
|
|
}
|
|
|
|
err = id.SatisfiesPrincipal(principal)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestOUPolicyPrincipalBadMSPID(t *testing.T) {
|
|
id, err := localMsp.GetDefaultSigningIdentity()
|
|
require.NoError(t, err)
|
|
|
|
cid, err := localMsp.(*bccspmsp).getCertificationChainIdentifier(id.GetPublicVersion())
|
|
require.NoError(t, err)
|
|
|
|
ou := &msp.OrganizationUnit{
|
|
OrganizationalUnitIdentifier: "COP",
|
|
MspIdentifier: "SampleOrgbarfbarf",
|
|
CertifiersIdentifier: cid,
|
|
}
|
|
bytes, err := proto.Marshal(ou)
|
|
require.NoError(t, err)
|
|
|
|
principal := &msp.MSPPrincipal{
|
|
PrincipalClassification: msp.MSPPrincipal_ORGANIZATION_UNIT,
|
|
Principal: bytes,
|
|
}
|
|
|
|
err = id.SatisfiesPrincipal(principal)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestOUPolicyPrincipalBadPath(t *testing.T) {
|
|
id, err := localMsp.GetDefaultSigningIdentity()
|
|
require.NoError(t, err)
|
|
|
|
ou := &msp.OrganizationUnit{
|
|
OrganizationalUnitIdentifier: "COP",
|
|
MspIdentifier: "SampleOrg",
|
|
CertifiersIdentifier: nil,
|
|
}
|
|
bytes, err := proto.Marshal(ou)
|
|
require.NoError(t, err)
|
|
|
|
principal := &msp.MSPPrincipal{
|
|
PrincipalClassification: msp.MSPPrincipal_ORGANIZATION_UNIT,
|
|
Principal: bytes,
|
|
}
|
|
|
|
err = id.SatisfiesPrincipal(principal)
|
|
require.Error(t, err)
|
|
|
|
ou = &msp.OrganizationUnit{
|
|
OrganizationalUnitIdentifier: "COP",
|
|
MspIdentifier: "SampleOrg",
|
|
CertifiersIdentifier: []byte{0, 1, 2, 3, 4},
|
|
}
|
|
bytes, err = proto.Marshal(ou)
|
|
require.NoError(t, err)
|
|
|
|
principal = &msp.MSPPrincipal{
|
|
PrincipalClassification: msp.MSPPrincipal_ORGANIZATION_UNIT,
|
|
Principal: bytes,
|
|
}
|
|
|
|
err = id.SatisfiesPrincipal(principal)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestPolicyPrincipalBogusType(t *testing.T) {
|
|
id, err := localMsp.GetDefaultSigningIdentity()
|
|
require.NoError(t, err)
|
|
|
|
principalBytes, err := proto.Marshal(&msp.MSPRole{Role: 35, MspIdentifier: "SampleOrg"})
|
|
require.NoError(t, err)
|
|
|
|
principal := &msp.MSPPrincipal{
|
|
PrincipalClassification: 35,
|
|
Principal: principalBytes,
|
|
}
|
|
|
|
err = id.SatisfiesPrincipal(principal)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestPolicyPrincipalBogusRole(t *testing.T) {
|
|
id, err := localMsp.GetDefaultSigningIdentity()
|
|
require.NoError(t, err)
|
|
|
|
principalBytes, err := proto.Marshal(&msp.MSPRole{Role: 35, MspIdentifier: "SampleOrg"})
|
|
require.NoError(t, err)
|
|
|
|
principal := &msp.MSPPrincipal{
|
|
PrincipalClassification: msp.MSPPrincipal_ROLE,
|
|
Principal: principalBytes,
|
|
}
|
|
|
|
err = id.SatisfiesPrincipal(principal)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestPolicyPrincipalWrongMSPID(t *testing.T) {
|
|
id, err := localMsp.GetDefaultSigningIdentity()
|
|
require.NoError(t, err)
|
|
|
|
principalBytes, err := proto.Marshal(&msp.MSPRole{Role: msp.MSPRole_MEMBER, MspIdentifier: "SampleOrgBARFBARF"})
|
|
require.NoError(t, err)
|
|
|
|
principal := &msp.MSPPrincipal{
|
|
PrincipalClassification: msp.MSPPrincipal_ROLE,
|
|
Principal: principalBytes,
|
|
}
|
|
|
|
err = id.SatisfiesPrincipal(principal)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestMemberPolicyPrincipal(t *testing.T) {
|
|
id, err := localMsp.GetDefaultSigningIdentity()
|
|
require.NoError(t, err)
|
|
|
|
principalBytes, err := proto.Marshal(&msp.MSPRole{Role: msp.MSPRole_MEMBER, MspIdentifier: "SampleOrg"})
|
|
require.NoError(t, err)
|
|
|
|
principal := &msp.MSPPrincipal{
|
|
PrincipalClassification: msp.MSPPrincipal_ROLE,
|
|
Principal: principalBytes,
|
|
}
|
|
|
|
err = id.SatisfiesPrincipal(principal)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestAdminPolicyPrincipal(t *testing.T) {
|
|
id, err := localMsp.GetDefaultSigningIdentity()
|
|
require.NoError(t, err)
|
|
|
|
principalBytes, err := proto.Marshal(&msp.MSPRole{Role: msp.MSPRole_ADMIN, MspIdentifier: "SampleOrg"})
|
|
require.NoError(t, err)
|
|
|
|
principal := &msp.MSPPrincipal{
|
|
PrincipalClassification: msp.MSPPrincipal_ROLE,
|
|
Principal: principalBytes,
|
|
}
|
|
|
|
err = id.SatisfiesPrincipal(principal)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// Combine one or more MSPPrincipals into a MSPPrincipal of type
|
|
// MSPPrincipal_COMBINED.
|
|
func createCombinedPrincipal(principals ...*msp.MSPPrincipal) (*msp.MSPPrincipal, error) {
|
|
if len(principals) == 0 {
|
|
return nil, errors.New("no principals in CombinedPrincipal")
|
|
}
|
|
principalsArray := append([]*msp.MSPPrincipal(nil), principals...)
|
|
combinedPrincipal := &msp.CombinedPrincipal{Principals: principalsArray}
|
|
combinedPrincipalBytes, err := proto.Marshal(combinedPrincipal)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
principalsCombined := &msp.MSPPrincipal{PrincipalClassification: msp.MSPPrincipal_COMBINED, Principal: combinedPrincipalBytes}
|
|
return principalsCombined, nil
|
|
}
|
|
|
|
func TestMultilevelAdminAndMemberPolicyPrincipal(t *testing.T) {
|
|
id, err := localMspV13.GetDefaultSigningIdentity()
|
|
require.NoError(t, err)
|
|
|
|
adminPrincipalBytes, err := proto.Marshal(&msp.MSPRole{Role: msp.MSPRole_ADMIN, MspIdentifier: "SampleOrg"})
|
|
require.NoError(t, err)
|
|
|
|
memberPrincipalBytes, err := proto.Marshal(&msp.MSPRole{Role: msp.MSPRole_MEMBER, MspIdentifier: "SampleOrg"})
|
|
require.NoError(t, err)
|
|
|
|
adminPrincipal := &msp.MSPPrincipal{
|
|
PrincipalClassification: msp.MSPPrincipal_ROLE,
|
|
Principal: adminPrincipalBytes,
|
|
}
|
|
|
|
memberPrincipal := &msp.MSPPrincipal{
|
|
PrincipalClassification: msp.MSPPrincipal_ROLE,
|
|
Principal: memberPrincipalBytes,
|
|
}
|
|
|
|
// CombinedPrincipal with Admin and Member principals
|
|
levelOneCombinedPrincipal, err := createCombinedPrincipal(adminPrincipal, memberPrincipal)
|
|
require.NoError(t, err)
|
|
err = id.SatisfiesPrincipal(levelOneCombinedPrincipal)
|
|
require.NoError(t, err)
|
|
|
|
// Nested CombinedPrincipal
|
|
levelTwoCombinedPrincipal, err := createCombinedPrincipal(levelOneCombinedPrincipal)
|
|
require.NoError(t, err)
|
|
err = id.SatisfiesPrincipal(levelTwoCombinedPrincipal)
|
|
require.NoError(t, err)
|
|
|
|
// Double nested CombinedPrincipal
|
|
levelThreeCombinedPrincipal, err := createCombinedPrincipal(levelTwoCombinedPrincipal)
|
|
require.NoError(t, err)
|
|
err = id.SatisfiesPrincipal(levelThreeCombinedPrincipal)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestMultilevelAdminAndMemberPolicyPrincipalPreV12(t *testing.T) {
|
|
id, err := localMspV11.GetDefaultSigningIdentity()
|
|
require.NoError(t, err)
|
|
|
|
adminPrincipalBytes, err := proto.Marshal(&msp.MSPRole{Role: msp.MSPRole_ADMIN, MspIdentifier: "SampleOrg"})
|
|
require.NoError(t, err)
|
|
|
|
memberPrincipalBytes, err := proto.Marshal(&msp.MSPRole{Role: msp.MSPRole_MEMBER, MspIdentifier: "SampleOrg"})
|
|
require.NoError(t, err)
|
|
|
|
adminPrincipal := &msp.MSPPrincipal{
|
|
PrincipalClassification: msp.MSPPrincipal_ROLE,
|
|
Principal: adminPrincipalBytes,
|
|
}
|
|
|
|
memberPrincipal := &msp.MSPPrincipal{
|
|
PrincipalClassification: msp.MSPPrincipal_ROLE,
|
|
Principal: memberPrincipalBytes,
|
|
}
|
|
|
|
// CombinedPrincipal with Admin and Member principals
|
|
levelOneCombinedPrincipal, err := createCombinedPrincipal(adminPrincipal, memberPrincipal)
|
|
require.NoError(t, err)
|
|
err = id.SatisfiesPrincipal(levelOneCombinedPrincipal)
|
|
require.Error(t, err)
|
|
|
|
// Nested CombinedPrincipal
|
|
levelTwoCombinedPrincipal, err := createCombinedPrincipal(levelOneCombinedPrincipal)
|
|
require.NoError(t, err)
|
|
err = id.SatisfiesPrincipal(levelTwoCombinedPrincipal)
|
|
require.Error(t, err)
|
|
|
|
// Double nested CombinedPrincipal
|
|
levelThreeCombinedPrincipal, err := createCombinedPrincipal(levelTwoCombinedPrincipal)
|
|
require.NoError(t, err)
|
|
err = id.SatisfiesPrincipal(levelThreeCombinedPrincipal)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestAdminPolicyPrincipalFails(t *testing.T) {
|
|
id, err := localMspV13.GetDefaultSigningIdentity()
|
|
require.NoError(t, err)
|
|
|
|
principalBytes, err := proto.Marshal(&msp.MSPRole{Role: msp.MSPRole_ADMIN, MspIdentifier: "SampleOrg"})
|
|
require.NoError(t, err)
|
|
|
|
principal := &msp.MSPPrincipal{
|
|
PrincipalClassification: msp.MSPPrincipal_ROLE,
|
|
Principal: principalBytes,
|
|
}
|
|
|
|
// remove the admin so validation will fail
|
|
localMspV13.(*bccspmsp).admins = make([]Identity, 0)
|
|
|
|
err = id.SatisfiesPrincipal(principal)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestMultilevelAdminAndMemberPolicyPrincipalFails(t *testing.T) {
|
|
id, err := localMspV13.GetDefaultSigningIdentity()
|
|
require.NoError(t, err)
|
|
|
|
adminPrincipalBytes, err := proto.Marshal(&msp.MSPRole{Role: msp.MSPRole_ADMIN, MspIdentifier: "SampleOrg"})
|
|
require.NoError(t, err)
|
|
|
|
memberPrincipalBytes, err := proto.Marshal(&msp.MSPRole{Role: msp.MSPRole_MEMBER, MspIdentifier: "SampleOrg"})
|
|
require.NoError(t, err)
|
|
|
|
adminPrincipal := &msp.MSPPrincipal{
|
|
PrincipalClassification: msp.MSPPrincipal_ROLE,
|
|
Principal: adminPrincipalBytes,
|
|
}
|
|
|
|
memberPrincipal := &msp.MSPPrincipal{
|
|
PrincipalClassification: msp.MSPPrincipal_ROLE,
|
|
Principal: memberPrincipalBytes,
|
|
}
|
|
|
|
// remove the admin so validation will fail
|
|
localMspV13.(*bccspmsp).admins = make([]Identity, 0)
|
|
|
|
// CombinedPrincipal with Admin and Member principals
|
|
levelOneCombinedPrincipal, err := createCombinedPrincipal(adminPrincipal, memberPrincipal)
|
|
require.NoError(t, err)
|
|
err = id.SatisfiesPrincipal(levelOneCombinedPrincipal)
|
|
require.Error(t, err)
|
|
|
|
// Nested CombinedPrincipal
|
|
levelTwoCombinedPrincipal, err := createCombinedPrincipal(levelOneCombinedPrincipal)
|
|
require.NoError(t, err)
|
|
err = id.SatisfiesPrincipal(levelTwoCombinedPrincipal)
|
|
require.Error(t, err)
|
|
|
|
// Double nested CombinedPrincipal
|
|
levelThreeCombinedPrincipal, err := createCombinedPrincipal(levelTwoCombinedPrincipal)
|
|
require.NoError(t, err)
|
|
err = id.SatisfiesPrincipal(levelThreeCombinedPrincipal)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestIdentityExpiresAt(t *testing.T) {
|
|
thisMSP := getLocalMSP(t, "testdata/expiration")
|
|
require.NotNil(t, thisMSP)
|
|
si, err := thisMSP.GetDefaultSigningIdentity()
|
|
require.NoError(t, err)
|
|
expirationDate := si.GetPublicVersion().ExpiresAt()
|
|
require.Equal(t, time.Date(2027, 8, 17, 12, 19, 48, 0, time.UTC), expirationDate)
|
|
}
|
|
|
|
func TestIdentityExpired(t *testing.T) {
|
|
cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
|
|
require.NoError(t, err)
|
|
|
|
expiredCertsDir := "testdata/expired"
|
|
conf, err := GetLocalMspConfig(expiredCertsDir, nil, "SampleOrg")
|
|
require.NoError(t, err)
|
|
|
|
thisMSP, err := newBccspMsp(MSPv1_0, cryptoProvider)
|
|
require.NoError(t, err)
|
|
|
|
ks, err := sw.NewFileBasedKeyStore(nil, filepath.Join(expiredCertsDir, "keystore"), true)
|
|
require.NoError(t, err)
|
|
|
|
csp, err := sw.NewWithParams(256, "SHA2", ks)
|
|
require.NoError(t, err)
|
|
thisMSP.(*bccspmsp).bccsp = csp
|
|
|
|
err = thisMSP.Setup(conf)
|
|
if err != nil {
|
|
require.Contains(t, err.Error(), "signing identity expired")
|
|
} else {
|
|
t.Fatal("Should have failed when loading expired certs")
|
|
}
|
|
}
|
|
|
|
func TestIdentityPolicyPrincipal(t *testing.T) {
|
|
id, err := localMsp.GetDefaultSigningIdentity()
|
|
require.NoError(t, err)
|
|
|
|
idSerialized, err := id.Serialize()
|
|
require.NoError(t, err)
|
|
|
|
principal := &msp.MSPPrincipal{
|
|
PrincipalClassification: msp.MSPPrincipal_IDENTITY,
|
|
Principal: idSerialized,
|
|
}
|
|
|
|
err = id.SatisfiesPrincipal(principal)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestIdentityPolicyPrincipalBadBytes(t *testing.T) {
|
|
id, err := localMsp.GetDefaultSigningIdentity()
|
|
require.NoError(t, err)
|
|
|
|
principal := &msp.MSPPrincipal{
|
|
PrincipalClassification: msp.MSPPrincipal_IDENTITY,
|
|
Principal: []byte("barf"),
|
|
}
|
|
|
|
err = id.SatisfiesPrincipal(principal)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestMSPOus(t *testing.T) {
|
|
// Set the OUIdentifiers
|
|
backup := localMsp.(*bccspmsp).ouIdentifiers
|
|
defer func() { localMsp.(*bccspmsp).ouIdentifiers = backup }()
|
|
sid, err := localMsp.GetDefaultSigningIdentity()
|
|
require.NoError(t, err)
|
|
sidBytes, err := sid.Serialize()
|
|
require.NoError(t, err)
|
|
id, err := localMsp.DeserializeIdentity(sidBytes)
|
|
require.NoError(t, err)
|
|
|
|
localMsp.(*bccspmsp).ouIdentifiers = map[string][][]byte{
|
|
"COP": {id.GetOrganizationalUnits()[0].CertifiersIdentifier},
|
|
}
|
|
require.NoError(t, localMsp.Validate(id))
|
|
|
|
id, err = localMsp.DeserializeIdentity(sidBytes)
|
|
require.NoError(t, err)
|
|
|
|
localMsp.(*bccspmsp).ouIdentifiers = map[string][][]byte{
|
|
"COP2": {id.GetOrganizationalUnits()[0].CertifiersIdentifier},
|
|
}
|
|
require.Error(t, localMsp.Validate(id))
|
|
|
|
id, err = localMsp.DeserializeIdentity(sidBytes)
|
|
require.NoError(t, err)
|
|
|
|
localMsp.(*bccspmsp).ouIdentifiers = map[string][][]byte{
|
|
"COP": {{0, 1, 2, 3, 4}},
|
|
}
|
|
require.Error(t, localMsp.Validate(id))
|
|
}
|
|
|
|
const othercert = `-----BEGIN CERTIFICATE-----
|
|
MIIDAzCCAqigAwIBAgIBAjAKBggqhkjOPQQDAjBsMQswCQYDVQQGEwJHQjEQMA4G
|
|
A1UECAwHRW5nbGFuZDEOMAwGA1UECgwFQmFyMTkxDjAMBgNVBAsMBUJhcjE5MQ4w
|
|
DAYDVQQDDAVCYXIxOTEbMBkGCSqGSIb3DQEJARYMQmFyMTktY2xpZW50MB4XDTE3
|
|
MDIwOTE2MDcxMFoXDTE4MDIxOTE2MDcxMFowfDELMAkGA1UEBhMCR0IxEDAOBgNV
|
|
BAgMB0VuZ2xhbmQxEDAOBgNVBAcMB0lwc3dpY2gxDjAMBgNVBAoMBUJhcjE5MQ4w
|
|
DAYDVQQLDAVCYXIxOTEOMAwGA1UEAwwFQmFyMTkxGTAXBgkqhkiG9w0BCQEWCkJh
|
|
cjE5LXBlZXIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQlRSnAyD+ND6qmaRV7
|
|
AS/BPJKX5dZt3gBe1v/RewOpc1zJeXQNWACAk0ae3mv5u9l0HxI6TXJIAQSwJACu
|
|
Rqsyo4IBKTCCASUwCQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMCBkAwMwYJYIZI
|
|
AYb4QgENBCYWJE9wZW5TU0wgR2VuZXJhdGVkIFNlcnZlciBDZXJ0aWZpY2F0ZTAd
|
|
BgNVHQ4EFgQUwHzbLJQMaWd1cpHdkSaEFxdKB1owgYsGA1UdIwSBgzCBgIAUYxFe
|
|
+cXOD5iQ223bZNdOuKCRiTKhZaRjMGExCzAJBgNVBAYTAkdCMRAwDgYDVQQIDAdF
|
|
bmdsYW5kMRAwDgYDVQQHDAdJcHN3aWNoMQ4wDAYDVQQKDAVCYXIxOTEOMAwGA1UE
|
|
CwwFQmFyMTkxDjAMBgNVBAMMBUJhcjE5ggEBMA4GA1UdDwEB/wQEAwIFoDATBgNV
|
|
HSUEDDAKBggrBgEFBQcDATAKBggqhkjOPQQDAgNJADBGAiEAuMq65lOaie4705Ol
|
|
Ow52DjbaO2YuIxK2auBCqNIu0gECIQCDoKdUQ/sa+9Ah1mzneE6iz/f/YFVWo4EP
|
|
HeamPGiDTQ==
|
|
-----END CERTIFICATE-----
|
|
`
|
|
|
|
func TestIdentityPolicyPrincipalFails(t *testing.T) {
|
|
id, err := localMsp.GetDefaultSigningIdentity()
|
|
require.NoError(t, err)
|
|
|
|
sid, err := NewSerializedIdentity("SampleOrg", []byte(othercert))
|
|
require.NoError(t, err)
|
|
|
|
principal := &msp.MSPPrincipal{
|
|
PrincipalClassification: msp.MSPPrincipal_IDENTITY,
|
|
Principal: sid,
|
|
}
|
|
|
|
err = id.SatisfiesPrincipal(principal)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
var (
|
|
conf *msp.MSPConfig
|
|
localMsp MSP
|
|
localMspV11 MSP
|
|
localMspV13 MSP
|
|
)
|
|
|
|
// Required because deleting the cert or msp options from localMsp causes parallel tests to fail
|
|
var (
|
|
localMspBad MSP
|
|
mspMgr MSPManager
|
|
)
|
|
|
|
func TestMain(m *testing.M) {
|
|
var err error
|
|
|
|
mspDir := configtest.GetDevMspDir()
|
|
conf, err = GetLocalMspConfig(mspDir, nil, "SampleOrg")
|
|
if err != nil {
|
|
fmt.Printf("Setup should have succeeded, got err %s instead", err)
|
|
os.Exit(-1)
|
|
}
|
|
|
|
localMsp, err = newBccspMsp(MSPv1_0, factory.GetDefault())
|
|
if err != nil {
|
|
fmt.Printf("Constructor for msp should have succeeded, got err %s instead", err)
|
|
os.Exit(-1)
|
|
}
|
|
|
|
localMspBad, err = newBccspMsp(MSPv1_0, factory.GetDefault())
|
|
if err != nil {
|
|
fmt.Printf("Constructor for msp should have succeeded, got err %s instead", err)
|
|
os.Exit(-1)
|
|
}
|
|
|
|
localMspV13, err = newBccspMsp(MSPv1_3, factory.GetDefault())
|
|
if err != nil {
|
|
fmt.Printf("Constructor for V1.3 msp should have succeeded, got err %s instead", err)
|
|
os.Exit(-1)
|
|
}
|
|
|
|
localMspV11, err = newBccspMsp(MSPv1_1, factory.GetDefault())
|
|
if err != nil {
|
|
fmt.Printf("Constructor for V1.1 msp should have succeeded, got err %s instead", err)
|
|
os.Exit(-1)
|
|
}
|
|
|
|
err = localMspV11.Setup(conf)
|
|
if err != nil {
|
|
fmt.Printf("Setup for V1.1 msp should have succeeded, got err %s instead", err)
|
|
os.Exit(-1)
|
|
}
|
|
|
|
err = localMspV13.Setup(conf)
|
|
if err != nil {
|
|
fmt.Printf("Setup for V1.3 msp should have succeeded, got err %s instead", err)
|
|
os.Exit(-1)
|
|
}
|
|
|
|
err = localMsp.Setup(conf)
|
|
if err != nil {
|
|
fmt.Printf("Setup for msp should have succeeded, got err %s instead", err)
|
|
os.Exit(-1)
|
|
}
|
|
|
|
err = localMspBad.Setup(conf)
|
|
if err != nil {
|
|
fmt.Printf("Setup for msp should have succeeded, got err %s instead", err)
|
|
os.Exit(-1)
|
|
}
|
|
|
|
mspMgr = NewMSPManager()
|
|
err = mspMgr.Setup([]MSP{localMsp})
|
|
if err != nil {
|
|
fmt.Printf("Setup for msp manager should have succeeded, got err %s instead", err)
|
|
os.Exit(-1)
|
|
}
|
|
|
|
id, err := localMsp.GetIdentifier()
|
|
if err != nil {
|
|
fmt.Println("Failed obtaining identifier for localMSP")
|
|
os.Exit(-1)
|
|
}
|
|
|
|
msps, err := mspMgr.GetMSPs()
|
|
if err != nil {
|
|
fmt.Println("Failed obtaining MSPs from MSP manager")
|
|
os.Exit(-1)
|
|
}
|
|
|
|
if msps[id] == nil {
|
|
fmt.Println("Couldn't find localMSP in MSP manager")
|
|
os.Exit(-1)
|
|
}
|
|
|
|
retVal := m.Run()
|
|
os.Exit(retVal)
|
|
}
|
|
|
|
func getIdentity(t *testing.T, path string) Identity {
|
|
mspDir := configtest.GetDevMspDir()
|
|
pems, err := getPemMaterialFromDir(filepath.Join(mspDir, path))
|
|
require.NoError(t, err)
|
|
|
|
id, _, err := localMsp.(*bccspmsp).getIdentityFromConf(pems[0])
|
|
require.NoError(t, err)
|
|
|
|
return id
|
|
}
|
|
|
|
func getLocalMSPWithVersionAndError(t *testing.T, dir string, version MSPVersion) (MSP, error) {
|
|
conf, err := GetLocalMspConfig(dir, nil, "SampleOrg")
|
|
require.NoError(t, err)
|
|
|
|
ks, err := sw.NewFileBasedKeyStore(nil, filepath.Join(dir, "keystore"), true)
|
|
require.NoError(t, err)
|
|
cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
|
|
require.NoError(t, err)
|
|
thisMSP, err := NewBccspMspWithKeyStore(version, ks, cryptoProvider)
|
|
require.NoError(t, err)
|
|
|
|
return thisMSP, thisMSP.Setup(conf)
|
|
}
|
|
|
|
func getLocalMSP(t *testing.T, dir string) MSP {
|
|
conf, err := GetLocalMspConfig(dir, nil, "SampleOrg")
|
|
require.NoError(t, err)
|
|
|
|
ks, err := sw.NewFileBasedKeyStore(nil, filepath.Join(dir, "keystore"), true)
|
|
require.NoError(t, err)
|
|
|
|
cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
|
|
require.NoError(t, err)
|
|
thisMSP, err := NewBccspMspWithKeyStore(MSPv1_0, ks, cryptoProvider)
|
|
require.NoError(t, err)
|
|
err = thisMSP.Setup(conf)
|
|
require.NoError(t, err)
|
|
|
|
return thisMSP
|
|
}
|
|
|
|
func getLocalMSPWithVersion(t *testing.T, dir string, version MSPVersion) MSP {
|
|
conf, err := GetLocalMspConfig(dir, nil, "SampleOrg")
|
|
require.NoError(t, err)
|
|
|
|
ks, err := sw.NewFileBasedKeyStore(nil, filepath.Join(dir, "keystore"), true)
|
|
require.NoError(t, err)
|
|
cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore())
|
|
require.NoError(t, err)
|
|
thisMSP, err := NewBccspMspWithKeyStore(version, ks, cryptoProvider)
|
|
require.NoError(t, err)
|
|
|
|
err = thisMSP.Setup(conf)
|
|
require.NoError(t, err)
|
|
|
|
return thisMSP
|
|
}
|
|
|
|
func TestCollectEmptyCombinedPrincipal(t *testing.T) {
|
|
var principalsArray []*msp.MSPPrincipal
|
|
combinedPrincipal := &msp.CombinedPrincipal{Principals: principalsArray}
|
|
combinedPrincipalBytes, err := proto.Marshal(combinedPrincipal)
|
|
require.NoError(t, err, "Error marshalling empty combined principal")
|
|
principalsCombined := &msp.MSPPrincipal{PrincipalClassification: msp.MSPPrincipal_COMBINED, Principal: combinedPrincipalBytes}
|
|
_, err = collectPrincipals(principalsCombined, MSPv1_3)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestCollectPrincipalContainingEmptyCombinedPrincipal(t *testing.T) {
|
|
var principalsArray []*msp.MSPPrincipal
|
|
combinedPrincipal := &msp.CombinedPrincipal{Principals: principalsArray}
|
|
combinedPrincipalBytes, err := proto.Marshal(combinedPrincipal)
|
|
require.NoError(t, err, "Error marshalling empty combined principal")
|
|
emptyPrincipal := &msp.MSPPrincipal{PrincipalClassification: msp.MSPPrincipal_COMBINED, Principal: combinedPrincipalBytes}
|
|
levelOneCombinedPrincipal, err := createCombinedPrincipal(emptyPrincipal)
|
|
require.NoError(t, err)
|
|
_, err = collectPrincipals(levelOneCombinedPrincipal, MSPv1_3)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestMSPIdentityIdentifier(t *testing.T) {
|
|
// testdata/mspid
|
|
// 1) a key and a signcert (used to populate the default signing identity) with the cert having a HighS signature
|
|
thisMSP := getLocalMSP(t, "testdata/mspid")
|
|
|
|
id, err := thisMSP.GetDefaultSigningIdentity()
|
|
require.NoError(t, err)
|
|
err = id.Validate()
|
|
require.NoError(t, err)
|
|
|
|
// Check that the identity identifier is computed with the respect to the lowS signature
|
|
|
|
idid := id.GetIdentifier()
|
|
require.NotNil(t, idid)
|
|
|
|
// Load and parse cacaert and signcert from folder
|
|
pems, err := getPemMaterialFromDir("testdata/mspid/cacerts")
|
|
require.NoError(t, err)
|
|
bl, _ := pem.Decode(pems[0])
|
|
require.NotNil(t, bl)
|
|
caCertFromFile, err := x509.ParseCertificate(bl.Bytes)
|
|
require.NoError(t, err)
|
|
|
|
pems, err = getPemMaterialFromDir("testdata/mspid/signcerts")
|
|
require.NoError(t, err)
|
|
bl, _ = pem.Decode(pems[0])
|
|
require.NotNil(t, bl)
|
|
certFromFile, err := x509.ParseCertificate(bl.Bytes)
|
|
require.NoError(t, err)
|
|
// Check that the certificates' raws are different, meaning that the identity has been sanitised
|
|
require.NotEqual(t, certFromFile.Raw, id.(*signingidentity).cert)
|
|
|
|
// Check that certFromFile is in HighS
|
|
_, S, err := utils.UnmarshalECDSASignature(certFromFile.Signature)
|
|
require.NoError(t, err)
|
|
lowS, err := utils.IsLowS(caCertFromFile.PublicKey.(*ecdsa.PublicKey), S)
|
|
require.NoError(t, err)
|
|
require.False(t, lowS)
|
|
|
|
// Check that id.(*signingidentity).cert is in LoswS
|
|
_, S, err = utils.UnmarshalECDSASignature(id.(*signingidentity).cert.Signature)
|
|
require.NoError(t, err)
|
|
lowS, err = utils.IsLowS(caCertFromFile.PublicKey.(*ecdsa.PublicKey), S)
|
|
require.NoError(t, err)
|
|
require.True(t, lowS)
|
|
|
|
// Compute the digest for certFromFile
|
|
thisBCCSPMsp := thisMSP.(*bccspmsp)
|
|
hashOpt, err := bccsp.GetHashOpt(thisBCCSPMsp.cryptoConfig.IdentityIdentifierHashFunction)
|
|
require.NoError(t, err)
|
|
digest, err := thisBCCSPMsp.bccsp.Hash(certFromFile.Raw, hashOpt)
|
|
require.NoError(t, err)
|
|
|
|
// Compare with the digest computed from the sanitised cert
|
|
require.NotEqual(t, idid.Id, hex.EncodeToString(digest))
|
|
}
|
|
|
|
func TestAnonymityIdentity(t *testing.T) {
|
|
id, err := localMspV13.GetDefaultSigningIdentity()
|
|
require.NoError(t, err)
|
|
|
|
principalBytes, err := proto.Marshal(&msp.MSPIdentityAnonymity{AnonymityType: msp.MSPIdentityAnonymity_NOMINAL})
|
|
require.NoError(t, err)
|
|
|
|
principal := &msp.MSPPrincipal{
|
|
PrincipalClassification: msp.MSPPrincipal_ANONYMITY,
|
|
Principal: principalBytes,
|
|
}
|
|
|
|
err = id.SatisfiesPrincipal(principal)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestAnonymityIdentityPreV12Fail(t *testing.T) {
|
|
id, err := localMspV11.GetDefaultSigningIdentity()
|
|
require.NoError(t, err)
|
|
|
|
principalBytes, err := proto.Marshal(&msp.MSPIdentityAnonymity{AnonymityType: msp.MSPIdentityAnonymity_NOMINAL})
|
|
require.NoError(t, err)
|
|
|
|
principal := &msp.MSPPrincipal{
|
|
PrincipalClassification: msp.MSPPrincipal_ANONYMITY,
|
|
Principal: principalBytes,
|
|
}
|
|
|
|
err = id.SatisfiesPrincipal(principal)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestAnonymityIdentityFail(t *testing.T) {
|
|
id, err := localMspV13.GetDefaultSigningIdentity()
|
|
require.NoError(t, err)
|
|
|
|
principalBytes, err := proto.Marshal(&msp.MSPIdentityAnonymity{AnonymityType: msp.MSPIdentityAnonymity_ANONYMOUS})
|
|
require.NoError(t, err)
|
|
|
|
principal := &msp.MSPPrincipal{
|
|
PrincipalClassification: msp.MSPPrincipal_ANONYMITY,
|
|
Principal: principalBytes,
|
|
}
|
|
|
|
err = id.SatisfiesPrincipal(principal)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestProviderTypeToString(t *testing.T) {
|
|
// Check that the provider type is found for FABRIC
|
|
pt := ProviderTypeToString(FABRIC)
|
|
require.Equal(t, "bccsp", pt)
|
|
|
|
// Check that the provider type is found for IDEMIX
|
|
pt = ProviderTypeToString(IDEMIX)
|
|
require.Equal(t, "idemix", pt)
|
|
|
|
// Check that the provider type is not found
|
|
pt = ProviderTypeToString(OTHER)
|
|
require.Equal(t, "", pt)
|
|
}
|