github.com/paul-lee-attorney/fabric-ca-1.4.7-gm@v0.0.0-20201120102036-c7ad827cf9ac/util/util.go (about)

     1  /*
     2  Copyright IBM Corp. 2016 All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  		 http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package util
    18  
    19  import (
    20  	"bytes"
    21  	"crypto/ecdsa"
    22  	"crypto/rsa"
    23  	"crypto/x509"
    24  	"encoding/base64"
    25  	"encoding/json"
    26  	"encoding/pem"
    27  	"fmt"
    28  	"io"
    29  	"io/ioutil"
    30  	"math/big"
    31  	mrand "math/rand"
    32  	"net/http"
    33  	"net/url"
    34  	"os"
    35  	"path"
    36  	"path/filepath"
    37  	"reflect"
    38  	"regexp"
    39  	"strings"
    40  	"testing"
    41  	"time"
    42  
    43  	"github.com/cloudflare/cfssl/log"
    44  	"github.com/paul-lee-attorney/fabric-2.1-gm/bccsp"
    45  	"github.com/paul-lee-attorney/gm/gmx509"
    46  	"github.com/paul-lee-attorney/gm/sm2"
    47  	"github.com/pkg/errors"
    48  	"github.com/spf13/viper"
    49  	"github.com/stretchr/testify/assert"
    50  	"golang.org/x/crypto/ocsp"
    51  )
    52  
    53  var (
    54  	rnd = mrand.NewSource(time.Now().UnixNano())
    55  	// ErrNotImplemented used to return errors for functions not implemented
    56  	ErrNotImplemented = errors.New("NOT YET IMPLEMENTED")
    57  )
    58  
    59  const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
    60  const (
    61  	letterIdxBits = 6                    // 6 bits to represent a letter index
    62  	letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
    63  	letterIdxMax  = 63 / letterIdxBits   // # of letter indices fitting in 63 bits
    64  )
    65  
    66  // RevocationReasonCodes is a map between string reason codes to integers as defined in RFC 5280
    67  var RevocationReasonCodes = map[string]int{
    68  	"unspecified":          ocsp.Unspecified,
    69  	"keycompromise":        ocsp.KeyCompromise,
    70  	"cacompromise":         ocsp.CACompromise,
    71  	"affiliationchanged":   ocsp.AffiliationChanged,
    72  	"superseded":           ocsp.Superseded,
    73  	"cessationofoperation": ocsp.CessationOfOperation,
    74  	"certificatehold":      ocsp.CertificateHold,
    75  	"removefromcrl":        ocsp.RemoveFromCRL,
    76  	"privilegewithdrawn":   ocsp.PrivilegeWithdrawn,
    77  	"aacompromise":         ocsp.AACompromise,
    78  }
    79  
    80  // SecretTag to tag a field as secret as in password, token
    81  const SecretTag = "mask"
    82  
    83  // URLRegex is the regular expression to check if a value is an URL
    84  var URLRegex = regexp.MustCompile("(ldap|http)s*://(\\S+):(\\S+)@")
    85  
    86  //ECDSASignature forms the structure for R and S value for ECDSA
    87  type ECDSASignature struct {
    88  	R, S *big.Int
    89  }
    90  
    91  // RandomString returns a random string
    92  func RandomString(n int) string {
    93  	b := make([]byte, n)
    94  
    95  	for i, cache, remain := n-1, rnd.Int63(), letterIdxMax; i >= 0; {
    96  		if remain == 0 {
    97  			cache, remain = rnd.Int63(), letterIdxMax
    98  		}
    99  		if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
   100  			b[i] = letterBytes[idx]
   101  			i--
   102  		}
   103  		cache >>= letterIdxBits
   104  		remain--
   105  	}
   106  
   107  	return string(b)
   108  }
   109  
   110  // RemoveQuotes removes outer quotes from a string if necessary
   111  func RemoveQuotes(str string) string {
   112  	if str == "" {
   113  		return str
   114  	}
   115  	if (strings.HasPrefix(str, "'") && strings.HasSuffix(str, "'")) ||
   116  		(strings.HasPrefix(str, "\"") && strings.HasSuffix(str, "\"")) {
   117  		str = str[1 : len(str)-1]
   118  	}
   119  	return str
   120  }
   121  
   122  // ReadFile reads a file
   123  func ReadFile(file string) ([]byte, error) {
   124  	return ioutil.ReadFile(file)
   125  }
   126  
   127  // WriteFile writes a file
   128  func WriteFile(file string, buf []byte, perm os.FileMode) error {
   129  	dir := path.Dir(file)
   130  	// Create the directory if it doesn't exist
   131  	if _, err := os.Stat(dir); os.IsNotExist(err) {
   132  		err = os.MkdirAll(dir, 0755)
   133  		if err != nil {
   134  			return errors.Wrapf(err, "Failed to create directory '%s' for file '%s'", dir, file)
   135  		}
   136  	}
   137  	return ioutil.WriteFile(file, buf, perm)
   138  }
   139  
   140  // FileExists checks to see if a file exists
   141  func FileExists(name string) bool {
   142  	if _, err := os.Stat(name); err != nil {
   143  		if os.IsNotExist(err) {
   144  			return false
   145  		}
   146  	}
   147  	return true
   148  }
   149  
   150  // Marshal to bytes
   151  func Marshal(from interface{}, what string) ([]byte, error) {
   152  	buf, err := json.Marshal(from)
   153  	if err != nil {
   154  		return nil, errors.Wrapf(err, "Failed to marshal %s", what)
   155  	}
   156  	return buf, nil
   157  }
   158  
   159  // Unmarshal from bytes
   160  func Unmarshal(from []byte, to interface{}, what string) error {
   161  	err := json.Unmarshal(from, to)
   162  	if err != nil {
   163  		return errors.Wrapf(err, "Failed to unmarshal %s", what)
   164  	}
   165  	return nil
   166  }
   167  
   168  // CreateToken creates a JWT-like token.
   169  // In a normal JWT token, the format of the token created is:
   170  //      <algorithm,claims,signature>
   171  // where each part is base64-encoded string separated by a period.
   172  // In this JWT-like token, there are two differences:
   173  // 1) the claims section is a certificate, so the format is:
   174  //      <certificate,signature>
   175  // 2) the signature uses the private key associated with the certificate,
   176  //    and the signature is across both the certificate and the "body" argument,
   177  //    which is the body of an HTTP request, though could be any arbitrary bytes.
   178  // @param cert The pem-encoded certificate
   179  // @param key The pem-encoded key
   180  // @param method http method of the request
   181  // @param uri URI of the request
   182  // @param body The body of an HTTP request
   183  func CreateToken(csp bccsp.BCCSP, cert []byte, key bccsp.Key, method, uri string, body []byte) (string, error) {
   184  	x509Cert, err := GetX509CertificateFromPEM(cert)
   185  	if err != nil {
   186  		return "", err
   187  	}
   188  	publicKey := x509Cert.PublicKey
   189  
   190  	var token string
   191  
   192  	//The RSA Key Gen is commented right now as there is bccsp does
   193  	switch publicKey.(type) {
   194  	/*
   195  		case *rsa.PublicKey:
   196  			token, err = GenRSAToken(csp, cert, key, body)
   197  			if err != nil {
   198  				return "", err
   199  			}
   200  	*/
   201  	case *ecdsa.PublicKey:
   202  		token, err = GenECDSAToken(csp, cert, key, method, uri, body)
   203  		if err != nil {
   204  			return "", err
   205  		}
   206  	}
   207  	return token, nil
   208  }
   209  
   210  //GenRSAToken signs the http body and cert with RSA using RSA private key
   211  // @csp : BCCSP instance
   212  /*
   213  func GenRSAToken(csp bccsp.BCCSP, cert []byte, key []byte, body []byte) (string, error) {
   214  	privKey, err := GetRSAPrivateKey(key)
   215  	if err != nil {
   216  		return "", err
   217  	}
   218  	b64body := B64Encode(body)
   219  	b64cert := B64Encode(cert)
   220  	bodyAndcert := b64body + "." + b64cert
   221  	hash := sha512.New384()
   222  	hash.Write([]byte(bodyAndcert))
   223  	h := hash.Sum(nil)
   224  	RSAsignature, err := rsa.SignPKCS1v15(rand.Reader, privKey, crypto.SHA384, h[:])
   225  	if err != nil {
   226  		return "", errors.Wrap(err, "Failed to rsa.SignPKCS1v15")
   227  	}
   228  	b64sig := B64Encode(RSAsignature)
   229  	token := b64cert + "." + b64sig
   230  
   231  	return  token, nil
   232  }
   233  */
   234  
   235  //GenECDSAToken signs the http body and cert with ECDSA using EC private key
   236  func GenECDSAToken(csp bccsp.BCCSP, cert []byte, key bccsp.Key, method, uri string, body []byte) (string, error) {
   237  	b64body := B64Encode(body)
   238  	b64cert := B64Encode(cert)
   239  	b64uri := B64Encode([]byte(uri))
   240  	payload := method + "." + b64uri + "." + b64body + "." + b64cert
   241  
   242  	return genECDSAToken(csp, key, b64cert, payload)
   243  }
   244  
   245  func genECDSAToken(csp bccsp.BCCSP, key bccsp.Key, b64cert, payload string) (string, error) {
   246  	digest, digestError := csp.Hash([]byte(payload), &bccsp.SHAOpts{})
   247  	if digestError != nil {
   248  		return "", errors.WithMessage(digestError, fmt.Sprintf("Hash failed on '%s'", payload))
   249  	}
   250  
   251  	ecSignature, err := csp.Sign(key, digest, nil)
   252  	if err != nil {
   253  		return "", errors.WithMessage(err, "BCCSP signature generation failure")
   254  	}
   255  	if len(ecSignature) == 0 {
   256  		return "", errors.New("BCCSP signature creation failed. Signature must be different than nil")
   257  	}
   258  
   259  	b64sig := B64Encode(ecSignature)
   260  	token := b64cert + "." + b64sig
   261  
   262  	return token, nil
   263  
   264  }
   265  
   266  // VerifyToken verifies token signed by either ECDSA or RSA and
   267  // returns the associated user ID
   268  func VerifyToken(csp bccsp.BCCSP, token string, method, uri string, body []byte, compMode1_3 bool) (*x509.Certificate, error) {
   269  
   270  	if csp == nil {
   271  		return nil, errors.New("BCCSP instance is not present")
   272  	}
   273  	x509Cert, b64Cert, b64Sig, err := DecodeToken(token)
   274  	if err != nil {
   275  		return nil, err
   276  	}
   277  	sig, err := B64Decode(b64Sig)
   278  	if err != nil {
   279  		return nil, errors.WithMessage(err, "Invalid base64 encoded signature in token")
   280  	}
   281  	b64Body := B64Encode(body)
   282  	b64uri := B64Encode([]byte(uri))
   283  	sigString := method + "." + b64uri + "." + b64Body + "." + b64Cert
   284  
   285  	pk2, err := csp.KeyImport(x509Cert, &bccsp.X509PublicKeyImportOpts{Temporary: true})
   286  	if err != nil {
   287  		return nil, errors.WithMessage(err, "Public Key import into BCCSP failed with error")
   288  	}
   289  	if pk2 == nil {
   290  		return nil, errors.New("Public Key Cannot be imported into BCCSP")
   291  	}
   292  
   293  	//bccsp.X509PublicKeyImportOpts
   294  	//Using default hash algo
   295  	digest, digestError := csp.Hash([]byte(sigString), &bccsp.SHAOpts{})
   296  	if digestError != nil {
   297  		return nil, errors.WithMessage(digestError, "Message digest failed")
   298  	}
   299  
   300  	valid, validErr := csp.Verify(pk2, sig, digest, nil)
   301  	if compMode1_3 && !valid {
   302  		log.Debugf("Failed to verify token based on new authentication header requirements: %s", err)
   303  		sigString := b64Body + "." + b64Cert
   304  		digest, digestError := csp.Hash([]byte(sigString), &bccsp.SHAOpts{})
   305  		if digestError != nil {
   306  			return nil, errors.WithMessage(digestError, "Message digest failed")
   307  		}
   308  		valid, validErr = csp.Verify(pk2, sig, digest, nil)
   309  	}
   310  
   311  	if validErr != nil {
   312  		return nil, errors.WithMessage(validErr, "Token signature validation failure")
   313  	}
   314  	if !valid {
   315  		return nil, errors.New("Token signature validation failed")
   316  	}
   317  
   318  	return x509Cert, nil
   319  }
   320  
   321  // DecodeToken extracts an X509 certificate and base64 encoded signature from a token
   322  func DecodeToken(token string) (*x509.Certificate, string, string, error) {
   323  	if token == "" {
   324  		return nil, "", "", errors.New("Invalid token; it is empty")
   325  	}
   326  	parts := strings.Split(token, ".")
   327  	if len(parts) != 2 {
   328  		return nil, "", "", errors.New("Invalid token format; expecting 2 parts separated by '.'")
   329  	}
   330  	b64cert := parts[0]
   331  	certDecoded, err := B64Decode(b64cert)
   332  	if err != nil {
   333  		return nil, "", "", errors.WithMessage(err, "Failed to decode base64 encoded x509 cert")
   334  	}
   335  	x509Cert, err := GetX509CertificateFromPEM(certDecoded)
   336  	if err != nil {
   337  		return nil, "", "", errors.WithMessage(err, "Error in parsing x509 certificate given block bytes")
   338  	}
   339  	return x509Cert, b64cert, parts[1], nil
   340  }
   341  
   342  //GetECPrivateKey get *ecdsa.PrivateKey from key pem
   343  func GetECPrivateKey(raw []byte) (*ecdsa.PrivateKey, error) {
   344  	decoded, _ := pem.Decode(raw)
   345  	if decoded == nil {
   346  		return nil, errors.New("Failed to decode the PEM-encoded ECDSA key")
   347  	}
   348  	ECprivKey, err := x509.ParseECPrivateKey(decoded.Bytes)
   349  	if err == nil {
   350  		return ECprivKey, nil
   351  	}
   352  	key, err2 := x509.ParsePKCS8PrivateKey(decoded.Bytes)
   353  	if err2 == nil {
   354  		switch key.(type) {
   355  		case *ecdsa.PrivateKey:
   356  			return key.(*ecdsa.PrivateKey), nil
   357  		case *rsa.PrivateKey:
   358  			return nil, errors.New("Expecting EC private key but found RSA private key")
   359  		default:
   360  			return nil, errors.New("Invalid private key type in PKCS#8 wrapping")
   361  		}
   362  	}
   363  	return nil, errors.Wrap(err2, "Failed parsing EC private key")
   364  }
   365  
   366  //GetSM2PrivateKey get *sm2.PrivateKey from key pem
   367  func GetSM2PrivateKey(raw []byte) (*sm2.PrivateKey, error) {
   368  	decoded, _ := pem.Decode(raw)
   369  	if decoded == nil {
   370  		return nil, errors.New("Failed to decode the PEM-encoded ECDSA key")
   371  	}
   372  	SM2privKey, err := gmx509.ParseSM2PrivateKey(decoded.Bytes)
   373  	if err == nil {
   374  		return ECprivKey, nil
   375  	}
   376  	key, err2 := gmx509.ParsePKCS8SM2PrivateKey(decoded.Bytes)
   377  	if err2 == nil {
   378  		return key.(*sm2.PrivateKey), nil
   379  	}
   380  	return nil, errors.Wrap(err2, "Failed parsing EC private key")
   381  }
   382  
   383  //GetRSAPrivateKey get *rsa.PrivateKey from key pem
   384  func GetRSAPrivateKey(raw []byte) (*rsa.PrivateKey, error) {
   385  	decoded, _ := pem.Decode(raw)
   386  	if decoded == nil {
   387  		return nil, errors.New("Failed to decode the PEM-encoded RSA key")
   388  	}
   389  	RSAprivKey, err := x509.ParsePKCS1PrivateKey(decoded.Bytes)
   390  	if err == nil {
   391  		return RSAprivKey, nil
   392  	}
   393  	key, err2 := x509.ParsePKCS8PrivateKey(decoded.Bytes)
   394  	if err2 == nil {
   395  		switch key.(type) {
   396  		case *ecdsa.PrivateKey:
   397  			return nil, errors.New("Expecting RSA private key but found EC private key")
   398  		case *rsa.PrivateKey:
   399  			return key.(*rsa.PrivateKey), nil
   400  		default:
   401  			return nil, errors.New("Invalid private key type in PKCS#8 wrapping")
   402  		}
   403  	}
   404  	return nil, errors.Wrap(err, "Failed parsing RSA private key")
   405  }
   406  
   407  // B64Encode base64 encodes bytes
   408  func B64Encode(buf []byte) string {
   409  	return base64.StdEncoding.EncodeToString(buf)
   410  }
   411  
   412  // B64Decode base64 decodes a string
   413  func B64Decode(str string) (buf []byte, err error) {
   414  	return base64.StdEncoding.DecodeString(str)
   415  }
   416  
   417  // StrContained returns true if 'str' is in 'strs'; otherwise return false
   418  func StrContained(str string, strs []string) bool {
   419  	for _, s := range strs {
   420  		if strings.ToLower(s) == strings.ToLower(str) {
   421  			return true
   422  		}
   423  	}
   424  	return false
   425  }
   426  
   427  // IsSubsetOf returns an error if there is something in 'small' that
   428  // is not in 'big'.  Both small and big are assumed to be comma-separated
   429  // strings.  All string comparisons are case-insensitive.
   430  // Examples:
   431  // 1) IsSubsetOf('a,B', 'A,B,C') returns nil
   432  // 2) IsSubsetOf('A,B,C', 'B,C') returns an error because A is not in the 2nd set.
   433  func IsSubsetOf(small, big string) error {
   434  	bigSet := strings.Split(big, ",")
   435  	smallSet := strings.Split(small, ",")
   436  	for _, s := range smallSet {
   437  		if s != "" && !StrContained(s, bigSet) {
   438  			return errors.Errorf("'%s' is not a member of '%s'", s, big)
   439  		}
   440  	}
   441  	return nil
   442  }
   443  
   444  // HTTPRequestToString returns a string for an HTTP request for debuggging
   445  func HTTPRequestToString(req *http.Request) string {
   446  	body, _ := ioutil.ReadAll(req.Body)
   447  	req.Body = ioutil.NopCloser(bytes.NewReader(body))
   448  	return fmt.Sprintf("%s %s\n%s",
   449  		req.Method, req.URL, string(body))
   450  }
   451  
   452  // HTTPResponseToString returns a string for an HTTP response for debuggging
   453  func HTTPResponseToString(resp *http.Response) string {
   454  	body, _ := ioutil.ReadAll(resp.Body)
   455  	resp.Body = ioutil.NopCloser(bytes.NewReader(body))
   456  	return fmt.Sprintf("statusCode=%d (%s)\n%s",
   457  		resp.StatusCode, resp.Status, string(body))
   458  }
   459  
   460  // CreateClientHome will create a home directory if it does not exist
   461  func CreateClientHome() (string, error) {
   462  	log.Debug("CreateHome")
   463  	home := filepath.Dir(GetDefaultConfigFile("fabric-ca-client"))
   464  
   465  	if _, err := os.Stat(home); err != nil {
   466  		if os.IsNotExist(err) {
   467  			err := os.MkdirAll(home, 0755)
   468  			if err != nil {
   469  				return "", err
   470  			}
   471  		}
   472  	}
   473  	return home, nil
   474  }
   475  
   476  // GetDefaultConfigFile gets the default path for the config file to display in usage message
   477  func GetDefaultConfigFile(cmdName string) string {
   478  	if cmdName == "fabric-ca-server" {
   479  		var fname = fmt.Sprintf("%s-config.yaml", cmdName)
   480  		// First check home env variables
   481  		home := "."
   482  		envs := []string{"FABRIC_CA_SERVER_HOME", "FABRIC_CA_HOME", "CA_CFG_PATH"}
   483  		for _, env := range envs {
   484  			envVal := os.Getenv(env)
   485  			if envVal != "" {
   486  				home = envVal
   487  				break
   488  			}
   489  		}
   490  		return path.Join(home, fname)
   491  	}
   492  
   493  	var fname = fmt.Sprintf("%s-config.yaml", cmdName)
   494  	// First check home env variables
   495  	var home string
   496  	envs := []string{"FABRIC_CA_CLIENT_HOME", "FABRIC_CA_HOME", "CA_CFG_PATH"}
   497  	for _, env := range envs {
   498  		envVal := os.Getenv(env)
   499  		if envVal != "" {
   500  			home = envVal
   501  			return path.Join(home, fname)
   502  		}
   503  	}
   504  
   505  	return path.Join(os.Getenv("HOME"), ".fabric-ca-client", fname)
   506  }
   507  
   508  // GetX509CertificateFromPEMFile gets an X509 certificate from a file
   509  func GetX509CertificateFromPEMFile(file string) (*x509.Certificate, error) {
   510  	pemBytes, err := ReadFile(file)
   511  	if err != nil {
   512  		return nil, err
   513  	}
   514  	x509Cert, err := GetX509CertificateFromPEM(pemBytes)
   515  	if err != nil {
   516  		return nil, errors.Wrapf(err, "Invalid certificate in '%s'", file)
   517  	}
   518  	return x509Cert, nil
   519  }
   520  
   521  // GetX509CertificateFromPEM get an X509 certificate from bytes in PEM format
   522  func GetX509CertificateFromPEM(cert []byte) (*x509.Certificate, error) {
   523  	block, _ := pem.Decode(cert)
   524  	if block == nil {
   525  		return nil, errors.New("Failed to PEM decode certificate")
   526  	}
   527  	x509Cert, err := gmx509.ParseCertificate(block.Bytes)
   528  	if err != nil {
   529  		return nil, errors.Wrap(err, "Error parsing certificate")
   530  	}
   531  	return x509Cert, nil
   532  }
   533  
   534  // GetX509CertificatesFromPEM returns X509 certificates from bytes in PEM format
   535  func GetX509CertificatesFromPEM(pemBytes []byte) ([]*x509.Certificate, error) {
   536  	chain := pemBytes
   537  	var certs []*x509.Certificate
   538  	for len(chain) > 0 {
   539  		var block *pem.Block
   540  		block, chain = pem.Decode(chain)
   541  		if block == nil {
   542  			break
   543  		}
   544  
   545  		cert, err := gmx509.ParseCertificate(block.Bytes)
   546  		if err != nil {
   547  			return nil, errors.Wrap(err, "Error parsing certificate")
   548  		}
   549  		certs = append(certs, cert)
   550  	}
   551  	return certs, nil
   552  }
   553  
   554  // GetCertificateDurationFromFile returns the validity duration for a certificate
   555  // in a file.
   556  func GetCertificateDurationFromFile(file string) (time.Duration, error) {
   557  	cert, err := GetX509CertificateFromPEMFile(file)
   558  	if err != nil {
   559  		return 0, err
   560  	}
   561  	return GetCertificateDuration(cert), nil
   562  }
   563  
   564  // GetCertificateDuration returns the validity duration for a certificate
   565  func GetCertificateDuration(cert *x509.Certificate) time.Duration {
   566  	return cert.NotAfter.Sub(cert.NotBefore)
   567  }
   568  
   569  // GetEnrollmentIDFromPEM returns the EnrollmentID from a PEM buffer
   570  func GetEnrollmentIDFromPEM(cert []byte) (string, error) {
   571  	x509Cert, err := GetX509CertificateFromPEM(cert)
   572  	if err != nil {
   573  		return "", err
   574  	}
   575  	return GetEnrollmentIDFromX509Certificate(x509Cert), nil
   576  }
   577  
   578  // GetEnrollmentIDFromX509Certificate returns the EnrollmentID from the X509 certificate
   579  func GetEnrollmentIDFromX509Certificate(cert *x509.Certificate) string {
   580  	return cert.Subject.CommonName
   581  }
   582  
   583  // MakeFileAbs makes 'file' absolute relative to 'dir' if not already absolute
   584  func MakeFileAbs(file, dir string) (string, error) {
   585  	if file == "" {
   586  		return "", nil
   587  	}
   588  	if filepath.IsAbs(file) {
   589  		return file, nil
   590  	}
   591  	path, err := filepath.Abs(filepath.Join(dir, file))
   592  	if err != nil {
   593  		return "", errors.Wrapf(err, "Failed making '%s' absolute based on '%s'", file, dir)
   594  	}
   595  	return path, nil
   596  }
   597  
   598  // MakeFileNamesAbsolute makes all file names in the list absolute, relative to home
   599  func MakeFileNamesAbsolute(files []*string, home string) error {
   600  	for _, filePtr := range files {
   601  		abs, err := MakeFileAbs(*filePtr, home)
   602  		if err != nil {
   603  			return err
   604  		}
   605  		*filePtr = abs
   606  	}
   607  	return nil
   608  }
   609  
   610  // Fatal logs a fatal message and exits
   611  func Fatal(format string, v ...interface{}) {
   612  	log.Fatalf(format, v...)
   613  	os.Exit(1)
   614  }
   615  
   616  // GetUser returns username and password from CLI input
   617  func GetUser(v *viper.Viper) (string, string, error) {
   618  	var fabricCAServerURL string
   619  	fabricCAServerURL = v.GetString("url")
   620  
   621  	URL, err := url.Parse(fabricCAServerURL)
   622  	if err != nil {
   623  		return "", "", err
   624  	}
   625  
   626  	user := URL.User
   627  	if user == nil {
   628  		return "", "", errors.New("No username and password provided as part of the Fabric CA server URL")
   629  	}
   630  
   631  	eid := user.Username()
   632  	if eid == "" {
   633  		return "", "", errors.New("No username provided as part of URL")
   634  	}
   635  
   636  	pass, _ := user.Password()
   637  	if pass == "" {
   638  		return "", "", errors.New("No password provided as part of URL")
   639  	}
   640  
   641  	return eid, pass, nil
   642  }
   643  
   644  // GetSerialAsHex returns the serial number from certificate as hex format
   645  func GetSerialAsHex(serial *big.Int) string {
   646  	hex := fmt.Sprintf("%x", serial)
   647  	return hex
   648  }
   649  
   650  // StructToString converts a struct to a string. If a field
   651  // has a 'secret' tag, it is masked in the returned string
   652  func StructToString(si interface{}) string {
   653  	rval := reflect.ValueOf(si).Elem()
   654  	tipe := rval.Type()
   655  	var buffer bytes.Buffer
   656  	buffer.WriteString("{ ")
   657  	for i := 0; i < rval.NumField(); i++ {
   658  		tf := tipe.Field(i)
   659  		if !rval.FieldByName(tf.Name).CanSet() {
   660  			continue // skip unexported fields
   661  		}
   662  		var fStr string
   663  		tagv := tf.Tag.Get(SecretTag)
   664  		if tagv == "password" || tagv == "username" {
   665  			fStr = fmt.Sprintf("%s:**** ", tf.Name)
   666  		} else if tagv == "url" {
   667  			val, ok := rval.Field(i).Interface().(string)
   668  			if ok {
   669  				val = GetMaskedURL(val)
   670  				fStr = fmt.Sprintf("%s:%v ", tf.Name, val)
   671  			} else {
   672  				fStr = fmt.Sprintf("%s:%v ", tf.Name, rval.Field(i).Interface())
   673  			}
   674  		} else {
   675  			fStr = fmt.Sprintf("%s:%v ", tf.Name, rval.Field(i).Interface())
   676  		}
   677  		buffer.WriteString(fStr)
   678  	}
   679  	buffer.WriteString(" }")
   680  	return buffer.String()
   681  }
   682  
   683  // GetMaskedURL returns masked URL. It masks username and password from the URL
   684  // if present
   685  func GetMaskedURL(url string) string {
   686  	matches := URLRegex.FindStringSubmatch(url)
   687  
   688  	// If there is a match, there should be four entries: 1 for
   689  	// the match and 3 for submatches
   690  	if len(matches) == 4 {
   691  		matchIdxs := URLRegex.FindStringSubmatchIndex(url)
   692  		matchStr := url[matchIdxs[0]:matchIdxs[1]]
   693  		for idx := 2; idx < len(matches); idx++ {
   694  			if matches[idx] != "" {
   695  				matchStr = strings.Replace(matchStr, matches[idx], "****", 1)
   696  			}
   697  		}
   698  		url = url[:matchIdxs[0]] + matchStr + url[matchIdxs[1]:len(url)]
   699  	}
   700  	return url
   701  }
   702  
   703  // NormalizeStringSlice checks for seperators
   704  func NormalizeStringSlice(slice []string) []string {
   705  	var normalizedSlice []string
   706  
   707  	for _, item := range slice {
   708  		// Remove surrounding brackets "[]" if specified
   709  		if strings.HasPrefix(item, "[") && strings.HasSuffix(item, "]") {
   710  			item = item[1 : len(item)-1]
   711  		}
   712  		// Split elements based on comma and add to normalized slice
   713  		elems := strings.Split(item, ",")
   714  		for _, elem := range elems {
   715  			normalizedSlice = append(normalizedSlice, strings.TrimSpace(elem))
   716  		}
   717  	}
   718  	return normalizedSlice
   719  }
   720  
   721  // NormalizeFileList provides absolute pathing for the list of files
   722  func NormalizeFileList(files []string, homeDir string) ([]string, error) {
   723  	var err error
   724  
   725  	files = NormalizeStringSlice(files)
   726  
   727  	for i, file := range files {
   728  		files[i], err = MakeFileAbs(file, homeDir)
   729  		if err != nil {
   730  			return nil, err
   731  		}
   732  	}
   733  
   734  	return files, nil
   735  }
   736  
   737  // CheckHostsInCert checks to see if host correctly inserted into certificate
   738  func CheckHostsInCert(certFile string, hosts ...string) error {
   739  	certBytes, err := ioutil.ReadFile(certFile)
   740  	if err != nil {
   741  		return errors.Wrapf(err, "Failed to read certificate file at '%s'", certFile)
   742  	}
   743  
   744  	cert, err := GetX509CertificateFromPEM(certBytes)
   745  	if err != nil {
   746  		return errors.Wrap(err, "Failed to get certificate")
   747  	}
   748  
   749  	// combine DNSNames and IPAddresses from cert
   750  	sans := cert.DNSNames
   751  	for _, ip := range cert.IPAddresses {
   752  		sans = append(sans, ip.String())
   753  	}
   754  	for _, host := range hosts {
   755  		if !containsString(sans, host) {
   756  			return errors.Errorf("Host '%s' was not found in the certificate in file '%s'", host, certFile)
   757  		}
   758  	}
   759  	return nil
   760  }
   761  
   762  func containsString(list []string, item string) bool {
   763  	for _, elem := range list {
   764  		if elem == item {
   765  			return true
   766  		}
   767  	}
   768  	return false
   769  }
   770  
   771  // Read reads from Reader into a byte array
   772  func Read(r io.Reader, data []byte) ([]byte, error) {
   773  	j := 0
   774  	for {
   775  		n, err := r.Read(data[j:])
   776  		j = j + n
   777  		if err != nil {
   778  			if err == io.EOF {
   779  				break
   780  			}
   781  			return nil, errors.Wrapf(err, "Read failure")
   782  		}
   783  
   784  		if (n == 0 && j == len(data)) || j > len(data) {
   785  			return nil, errors.New("Size of requested data is too large")
   786  		}
   787  	}
   788  
   789  	return data[:j], nil
   790  }
   791  
   792  // Hostname name returns the hostname of the machine
   793  func Hostname() string {
   794  	hostname, _ := os.Hostname()
   795  	if hostname == "" {
   796  		hostname = "localhost"
   797  	}
   798  	return hostname
   799  }
   800  
   801  // ValidateAndReturnAbsConf checks to see that there are no conflicts between the
   802  // configuration file path and home directory. If no conflicts, returns back the absolute
   803  // path for the configuration file and home directory.
   804  func ValidateAndReturnAbsConf(configFilePath, homeDir, cmdName string) (string, string, error) {
   805  	var err error
   806  	var homeDirSet bool
   807  	var configFileSet bool
   808  
   809  	defaultConfig := GetDefaultConfigFile(cmdName) // Get the default configuration
   810  
   811  	if configFilePath == "" {
   812  		configFilePath = defaultConfig // If no config file path specified, use the default configuration file
   813  	} else {
   814  		configFileSet = true
   815  	}
   816  
   817  	if homeDir == "" {
   818  		homeDir = filepath.Dir(defaultConfig) // If no home directory specified, use the default directory
   819  	} else {
   820  		homeDirSet = true
   821  	}
   822  
   823  	// Make the home directory absolute
   824  	homeDir, err = filepath.Abs(homeDir)
   825  	if err != nil {
   826  		return "", "", errors.Wrap(err, "Failed to get full path of config file")
   827  	}
   828  	homeDir = strings.TrimRight(homeDir, "/")
   829  
   830  	if configFileSet && homeDirSet {
   831  		log.Warning("Using both --config and --home CLI flags; --config will take precedence")
   832  	}
   833  
   834  	if configFileSet {
   835  		configFilePath, err = filepath.Abs(configFilePath)
   836  		if err != nil {
   837  			return "", "", errors.Wrap(err, "Failed to get full path of configuration file")
   838  		}
   839  		return configFilePath, filepath.Dir(configFilePath), nil
   840  	}
   841  
   842  	configFile := filepath.Join(homeDir, filepath.Base(defaultConfig)) // Join specified home directory with default config file name
   843  	return configFile, homeDir, nil
   844  }
   845  
   846  // GetSliceFromList will return a slice from a list
   847  func GetSliceFromList(split string, delim string) []string {
   848  	return strings.Split(strings.Replace(split, " ", "", -1), delim)
   849  }
   850  
   851  // ListContains looks through a comma separated list to see if a string exists
   852  func ListContains(list, find string) bool {
   853  	items := strings.Split(list, ",")
   854  	for _, item := range items {
   855  		item = strings.TrimPrefix(item, " ")
   856  		if item == find {
   857  			return true
   858  		}
   859  	}
   860  	return false
   861  }
   862  
   863  //TODO:  move these out of production code
   864  
   865  // FatalError will check to see if an error occured if so it will cause the test cases exit
   866  func FatalError(t *testing.T, err error, msg string, args ...interface{}) {
   867  	if len(args) > 0 {
   868  		msg = fmt.Sprintf(msg, args)
   869  	}
   870  	if !assert.NoError(t, err, msg) {
   871  		t.Fatal(msg)
   872  	}
   873  }
   874  
   875  // ErrorContains will check to see if an error occurred, if so it will check that it contains
   876  // the appropriate error message
   877  func ErrorContains(t *testing.T, err error, contains, msg string, args ...interface{}) {
   878  	if len(args) > 0 {
   879  		msg = fmt.Sprintf(msg, args)
   880  	}
   881  	if assert.Error(t, err, msg) {
   882  		assert.Contains(t, err.Error(), contains)
   883  	}
   884  }