github.com/hyperledger-labs/bdls@v2.1.1+incompatible/cmd/idemixgen/main.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package main
     8  
     9  // idemixgen is a command line tool that generates the CA's keys and
    10  // generates MSP configs for siging and for verification
    11  // This tool can be used to setup the peers and CA to support
    12  // the Identity Mixer MSP
    13  
    14  import (
    15  	"crypto/ecdsa"
    16  	"crypto/x509"
    17  	"encoding/pem"
    18  	"fmt"
    19  	"io/ioutil"
    20  	"os"
    21  	"path/filepath"
    22  
    23  	"github.com/golang/protobuf/proto"
    24  	"github.com/hyperledger/fabric/common/tools/idemixgen/idemixca"
    25  	"github.com/hyperledger/fabric/common/tools/idemixgen/metadata"
    26  	"github.com/hyperledger/fabric/idemix"
    27  	"github.com/hyperledger/fabric/msp"
    28  	"github.com/pkg/errors"
    29  	"gopkg.in/alecthomas/kingpin.v2"
    30  )
    31  
    32  const (
    33  	IdemixDirIssuer             = "ca"
    34  	IdemixConfigIssuerSecretKey = "IssuerSecretKey"
    35  	IdemixConfigRevocationKey   = "RevocationKey"
    36  )
    37  
    38  // command line flags
    39  var (
    40  	app = kingpin.New("idemixgen", "Utility for generating key material to be used with the Identity Mixer MSP in Hyperledger Fabric")
    41  
    42  	outputDir = app.Flag("output", "The output directory in which to place artifacts").Default("idemix-config").String()
    43  
    44  	genIssuerKey            = app.Command("ca-keygen", "Generate CA key material")
    45  	genSignerConfig         = app.Command("signerconfig", "Generate a default signer for this Idemix MSP")
    46  	genCAInput              = genSignerConfig.Flag("ca-input", "The folder where CA's secrets are stored").String()
    47  	genCredOU               = genSignerConfig.Flag("org-unit", "The Organizational Unit of the default signer").Short('u').String()
    48  	genCredIsAdmin          = genSignerConfig.Flag("admin", "Make the default signer admin").Short('a').Bool()
    49  	genCredEnrollmentId     = genSignerConfig.Flag("enrollmentId", "The enrollment id of the default signer").Short('e').String()
    50  	genCredRevocationHandle = genSignerConfig.Flag("revocationHandle", "The handle used to revoke this signer").Short('r').Int()
    51  
    52  	version = app.Command("version", "Show version information")
    53  )
    54  
    55  func main() {
    56  	app.HelpFlag.Short('h')
    57  
    58  	switch kingpin.MustParse(app.Parse(os.Args[1:])) {
    59  
    60  	case genIssuerKey.FullCommand():
    61  		isk, ipk, err := idemixca.GenerateIssuerKey()
    62  		handleError(err)
    63  
    64  		revocationKey, err := idemix.GenerateLongTermRevocationKey()
    65  		handleError(err)
    66  		encodedRevocationSK, err := x509.MarshalECPrivateKey(revocationKey)
    67  		handleError(err)
    68  		pemEncodedRevocationSK := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: encodedRevocationSK})
    69  		handleError(err)
    70  		encodedRevocationPK, err := x509.MarshalPKIXPublicKey(revocationKey.Public())
    71  		handleError(err)
    72  		pemEncodedRevocationPK := pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: encodedRevocationPK})
    73  
    74  		// Prevent overwriting the existing key
    75  		path := filepath.Join(*outputDir, IdemixDirIssuer)
    76  		checkDirectoryNotExists(path, fmt.Sprintf("Directory %s already exists", path))
    77  
    78  		path = filepath.Join(*outputDir, msp.IdemixConfigDirMsp)
    79  		checkDirectoryNotExists(path, fmt.Sprintf("Directory %s already exists", path))
    80  
    81  		// write private and public keys to the file
    82  		handleError(os.MkdirAll(filepath.Join(*outputDir, IdemixDirIssuer), 0770))
    83  		handleError(os.MkdirAll(filepath.Join(*outputDir, msp.IdemixConfigDirMsp), 0770))
    84  		writeFile(filepath.Join(*outputDir, IdemixDirIssuer, IdemixConfigIssuerSecretKey), isk)
    85  		writeFile(filepath.Join(*outputDir, IdemixDirIssuer, IdemixConfigRevocationKey), pemEncodedRevocationSK)
    86  		writeFile(filepath.Join(*outputDir, IdemixDirIssuer, msp.IdemixConfigFileIssuerPublicKey), ipk)
    87  		writeFile(filepath.Join(*outputDir, msp.IdemixConfigDirMsp, msp.IdemixConfigFileRevocationPublicKey), pemEncodedRevocationPK)
    88  		writeFile(filepath.Join(*outputDir, msp.IdemixConfigDirMsp, msp.IdemixConfigFileIssuerPublicKey), ipk)
    89  
    90  	case genSignerConfig.FullCommand():
    91  		roleMask := 0
    92  		if *genCredIsAdmin {
    93  			roleMask = msp.GetRoleMaskFromIdemixRole(msp.ADMIN)
    94  		} else {
    95  			roleMask = msp.GetRoleMaskFromIdemixRole(msp.MEMBER)
    96  		}
    97  		if *genCAInput == "" {
    98  			genCAInput = outputDir
    99  		}
   100  		ipk, ipkRaw := readIssuerKey()
   101  		rsk := readRevocationKey()
   102  		rpk := readRevocationPublicKey()
   103  
   104  		config, err := idemixca.GenerateSignerConfig(
   105  			roleMask,
   106  			*genCredOU,
   107  			*genCredEnrollmentId,
   108  			*genCredRevocationHandle,
   109  			ipk, rsk,
   110  		)
   111  		handleError(err)
   112  
   113  		path := filepath.Join(*outputDir, msp.IdemixConfigDirUser)
   114  		checkDirectoryNotExists(path, fmt.Sprintf("This MSP config already contains a directory \"%s\"", path))
   115  
   116  		// Write config to file
   117  		handleError(os.MkdirAll(filepath.Join(*outputDir, msp.IdemixConfigDirUser), 0770))
   118  		writeFile(filepath.Join(*outputDir, msp.IdemixConfigDirUser, msp.IdemixConfigFileSigner), config)
   119  
   120  		// Write CA public info in case genCAInput != outputDir
   121  		if *genCAInput != *outputDir {
   122  			handleError(os.MkdirAll(filepath.Join(*outputDir, msp.IdemixConfigDirMsp), 0770))
   123  			writeFile(filepath.Join(*outputDir, msp.IdemixConfigDirMsp, msp.IdemixConfigFileRevocationPublicKey), rpk)
   124  			writeFile(filepath.Join(*outputDir, msp.IdemixConfigDirMsp, msp.IdemixConfigFileIssuerPublicKey), ipkRaw)
   125  		}
   126  
   127  	case version.FullCommand():
   128  		printVersion()
   129  
   130  	}
   131  }
   132  
   133  func printVersion() {
   134  	fmt.Println(metadata.GetVersionInfo())
   135  }
   136  
   137  // writeFile writes bytes to a file and panics in case of an error
   138  func writeFile(path string, contents []byte) {
   139  	handleError(ioutil.WriteFile(path, contents, 0640))
   140  }
   141  
   142  // readIssuerKey reads the issuer key from the current directory
   143  func readIssuerKey() (*idemix.IssuerKey, []byte) {
   144  	path := filepath.Join(*genCAInput, IdemixDirIssuer, IdemixConfigIssuerSecretKey)
   145  	isk, err := ioutil.ReadFile(path)
   146  	if err != nil {
   147  		handleError(errors.Wrapf(err, "failed to open issuer secret key file: %s", path))
   148  	}
   149  	path = filepath.Join(*genCAInput, IdemixDirIssuer, msp.IdemixConfigFileIssuerPublicKey)
   150  	ipkBytes, err := ioutil.ReadFile(path)
   151  	if err != nil {
   152  		handleError(errors.Wrapf(err, "failed to open issuer public key file: %s", path))
   153  	}
   154  	ipk := &idemix.IssuerPublicKey{}
   155  	handleError(proto.Unmarshal(ipkBytes, ipk))
   156  	key := &idemix.IssuerKey{Isk: isk, Ipk: ipk}
   157  
   158  	return key, ipkBytes
   159  }
   160  
   161  func readRevocationKey() *ecdsa.PrivateKey {
   162  	path := filepath.Join(*genCAInput, IdemixDirIssuer, IdemixConfigRevocationKey)
   163  	keyBytes, err := ioutil.ReadFile(path)
   164  	if err != nil {
   165  		handleError(errors.Wrapf(err, "failed to open revocation secret key file: %s", path))
   166  	}
   167  
   168  	block, _ := pem.Decode(keyBytes)
   169  	if block == nil {
   170  		handleError(errors.Errorf("failed to decode ECDSA private key"))
   171  	}
   172  	key, err := x509.ParseECPrivateKey(block.Bytes)
   173  	handleError(err)
   174  
   175  	return key
   176  }
   177  
   178  func readRevocationPublicKey() []byte {
   179  	path := filepath.Join(*genCAInput, msp.IdemixConfigDirMsp, msp.IdemixConfigFileRevocationPublicKey)
   180  	keyBytes, err := ioutil.ReadFile(path)
   181  	if err != nil {
   182  		handleError(errors.Wrapf(err, "failed to open revocation secret key file: %s", path))
   183  	}
   184  
   185  	return keyBytes
   186  }
   187  
   188  // checkDirectoryNotExists checks whether a directory with the given path already exists and exits if this is the case
   189  func checkDirectoryNotExists(path string, errorMessage string) {
   190  	_, err := os.Stat(path)
   191  	if err == nil {
   192  		handleError(errors.New(errorMessage))
   193  	}
   194  }
   195  
   196  func handleError(err error) {
   197  	if err != nil {
   198  		fmt.Fprintln(os.Stderr, err)
   199  		os.Exit(1)
   200  	}
   201  }