-
Notifications
You must be signed in to change notification settings - Fork 8.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge "[FAB-5674] Adds tool to generate idemix crypto material"
- Loading branch information
Showing
29 changed files
with
437 additions
and
164 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
/* | ||
Copyright IBM Corp. All Rights Reserved. | ||
SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package idemixca | ||
|
||
import ( | ||
"github.com/golang/protobuf/proto" | ||
"github.com/hyperledger/fabric/idemix" | ||
"github.com/hyperledger/fabric/msp" | ||
m "github.com/hyperledger/fabric/protos/msp" | ||
amcl "github.com/manudrijvers/amcl/go" | ||
"github.com/pkg/errors" | ||
) | ||
|
||
// GenerateIssuerKey invokes Idemix library to generate an issuer (CA) signing key pair | ||
// currently two attributes are supported by the issuer: | ||
// AttributeNameOU is the organization unit name | ||
// AttributeNameRole is the role (member or admin) name | ||
// Generated keys are serialized to bytes | ||
func GenerateIssuerKey() ([]byte, []byte, error) { | ||
rng, err := idemix.GetRand() | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
AttributeNames := []string{msp.AttributeNameOU, msp.AttributeNameRole} | ||
key, err := idemix.NewIssuerKey(AttributeNames, rng) | ||
if err != nil { | ||
return nil, nil, errors.WithMessage(err, "cannot generate CA key") | ||
} | ||
ipkSerialized, err := proto.Marshal(key.IPk) | ||
|
||
return key.ISk, ipkSerialized, err | ||
} | ||
|
||
// GenerateMSPConfig creates a new MSP config | ||
// If the new MSP config contains a signer then | ||
// it generates a fresh user secret and issues a credential | ||
// with two attributes (described above) | ||
// using the CA's key pair from the file | ||
// If the new MSP config does not contain a signer | ||
// (meaning it is used only for verification) | ||
// then only a public key of the CA (issuer) is added to the MSP config (besides the name) | ||
func GenerateSignerConfig(isAdmin bool, ouString string, key *idemix.IssuerKey) ([]byte, error) { | ||
attrs := make([]*amcl.BIG, 2) | ||
|
||
if ouString == "" { | ||
return nil, errors.Errorf("the OU attribute value is empty") | ||
} | ||
|
||
role := m.MSPRole_MEMBER | ||
|
||
if isAdmin { | ||
role = m.MSPRole_ADMIN | ||
} | ||
|
||
attrs[0] = idemix.HashModOrder([]byte(ouString)) | ||
attrs[1] = amcl.NewBIGint(int(role)) | ||
|
||
rng, err := idemix.GetRand() | ||
if err != nil { | ||
return nil, errors.WithMessage(err, "Error getting PRNG") | ||
} | ||
sk := idemix.RandModOrder(rng) | ||
randCred := idemix.RandModOrder(rng) | ||
ni := idemix.RandModOrder(rng) | ||
msg := idemix.NewCredRequest(sk, randCred, ni, key.IPk, rng) | ||
cred, err := idemix.NewCredential(key, msg, attrs, rng) | ||
if err != nil { | ||
return nil, errors.WithMessage(err, "failed to generate a credential") | ||
} | ||
cred.Complete(randCred) | ||
|
||
credBytes, err := proto.Marshal(cred) | ||
if err != nil { | ||
return nil, errors.WithMessage(err, "failed to marshal credential") | ||
} | ||
|
||
signer := &m.IdemixMSPSignerConfig{ | ||
credBytes, | ||
idemix.BigToBytes(sk), | ||
ouString, | ||
isAdmin, | ||
} | ||
return proto.Marshal(signer) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
/* | ||
Copyright IBM Corp. All Rights Reserved. | ||
SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package idemixca | ||
|
||
import ( | ||
"io/ioutil" | ||
"os" | ||
"path/filepath" | ||
"testing" | ||
|
||
"github.com/golang/protobuf/proto" | ||
"github.com/hyperledger/fabric/idemix" | ||
m "github.com/hyperledger/fabric/msp" | ||
"github.com/pkg/errors" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
var testDir = filepath.Join(os.TempDir(), "idemixca-test") | ||
var configName = "testconfig" | ||
|
||
func TestIdemixCa(t *testing.T) { | ||
cleanup() | ||
|
||
isk, ipkBytes, err := GenerateIssuerKey() | ||
assert.NoError(t, err) | ||
|
||
ipk := &idemix.IssuerPublicKey{} | ||
err = proto.Unmarshal(ipkBytes, ipk) | ||
assert.NoError(t, err) | ||
|
||
writeVerifierToFile(ipkBytes) | ||
|
||
key := &idemix.IssuerKey{isk, ipk} | ||
|
||
conf, err := GenerateSignerConfig(false, "OU1", key) | ||
assert.NoError(t, err) | ||
cleanupSigner() | ||
assert.NoError(t, writeSignerToFile(conf)) | ||
assert.NoError(t, setupMSP()) | ||
|
||
conf, err = GenerateSignerConfig(true, "OU1", key) | ||
assert.NoError(t, err) | ||
cleanupSigner() | ||
assert.NoError(t, writeSignerToFile(conf)) | ||
assert.NoError(t, setupMSP()) | ||
|
||
// Without the verifier dir present, setup should give an error | ||
cleanupVerifier() | ||
assert.Error(t, setupMSP()) | ||
|
||
_, err = GenerateSignerConfig(true, "", key) | ||
assert.EqualError(t, err, "the OU attribute value is empty") | ||
} | ||
|
||
func cleanup() error { | ||
// clean up any previous files | ||
err := os.RemoveAll(testDir) | ||
if err != nil { | ||
return nil | ||
} | ||
return os.Mkdir(testDir, os.ModePerm) | ||
} | ||
|
||
func cleanupSigner() { | ||
os.RemoveAll(filepath.Join(testDir, m.IdemixConfigDirUser)) | ||
} | ||
|
||
func cleanupVerifier() { | ||
os.RemoveAll(filepath.Join(testDir, m.IdemixConfigDirMsp)) | ||
} | ||
|
||
func writeVerifierToFile(ipkBytes []byte) error { | ||
err := os.Mkdir(filepath.Join(testDir, m.IdemixConfigDirMsp), os.ModePerm) | ||
if err != nil { | ||
return err | ||
} | ||
return ioutil.WriteFile(filepath.Join(testDir, m.IdemixConfigDirMsp, m.IdemixConfigFileIssuerPublicKey), ipkBytes, 0644) | ||
} | ||
|
||
func writeSignerToFile(signerBytes []byte) error { | ||
err := os.Mkdir(filepath.Join(testDir, m.IdemixConfigDirUser), os.ModePerm) | ||
if err != nil { | ||
return err | ||
} | ||
return ioutil.WriteFile(filepath.Join(testDir, m.IdemixConfigDirUser, m.IdemixConfigFileSigner), signerBytes, 0644) | ||
} | ||
|
||
// setupMSP tests whether we can successfully setup an idemix msp | ||
// with the generated config bytes | ||
func setupMSP() error { | ||
// setup an idemix msp from the test directory | ||
msp, err := m.New(&m.IdemixNewOpts{NewBaseOpts: m.NewBaseOpts{Version: m.MSPv1_0}}) | ||
if err != nil { | ||
return errors.Wrap(err, "Getting MSP failed") | ||
} | ||
mspConfig, err := m.GetIdemixMspConfig(testDir, "TestName") | ||
|
||
return msp.Setup(mspConfig) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
/* | ||
Copyright IBM Corp. All Rights Reserved. | ||
SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package main | ||
|
||
// idemixgen is a command line tool that generates the CA's keys and | ||
// generates MSP configs for siging and for verification | ||
// This tool can be used to setup the peers and CA to support | ||
// the Identity Mixer MSP | ||
|
||
import ( | ||
"fmt" | ||
"io/ioutil" | ||
"os" | ||
"path/filepath" | ||
|
||
"github.com/golang/protobuf/proto" | ||
"github.com/hyperledger/fabric/common/tools/idemixgen/idemixca" | ||
"github.com/hyperledger/fabric/idemix" | ||
"github.com/hyperledger/fabric/msp" | ||
"github.com/hyperledger/fabric/orderer/common/metadata" | ||
"github.com/pkg/errors" | ||
"gopkg.in/alecthomas/kingpin.v2" | ||
) | ||
|
||
const ( | ||
IdemixDirIssuer = "ca" | ||
IdemixConfigIssuerSecretKey = "IssuerSecretKey" | ||
) | ||
|
||
// command line flags | ||
var ( | ||
app = kingpin.New("idemixgen", "Utility for generating key material to be used with the Identity Mixer MSP in Hyperledger Fabric") | ||
|
||
genIssuerKey = app.Command("ca-keygen", "Generate CA key material") | ||
genSignerConfig = app.Command("signerconfig", "Generate a default signer for this Idemix MSP") | ||
genCredOU = genSignerConfig.Flag("org-unit", "The Organizational Unit of the default signer").Short('u').String() | ||
genCredIsAdmin = genSignerConfig.Flag("admin", "Make the default signer admin").Short('a').Bool() | ||
|
||
version = app.Command("version", "Show version information") | ||
) | ||
|
||
func main() { | ||
app.HelpFlag.Short('h') | ||
|
||
switch kingpin.MustParse(app.Parse(os.Args[1:])) { | ||
|
||
case genIssuerKey.FullCommand(): | ||
isk, ipk, err := idemixca.GenerateIssuerKey() | ||
handleError(err) | ||
|
||
// Prevent overwriting the existing key | ||
path := filepath.Join(IdemixDirIssuer) | ||
checkDirectoryNotExists(path, fmt.Sprintf("Directory %s already exists", path)) | ||
|
||
path = msp.IdemixConfigDirMsp | ||
checkDirectoryNotExists(path, fmt.Sprintf("Directory %s already exists", path)) | ||
|
||
// write private and public keys to the file | ||
handleError(os.Mkdir(IdemixDirIssuer, 0770)) | ||
handleError(os.Mkdir(msp.IdemixConfigDirMsp, 0770)) | ||
writeFile(filepath.Join(IdemixDirIssuer, IdemixConfigIssuerSecretKey), isk) | ||
writeFile(filepath.Join(IdemixDirIssuer, msp.IdemixConfigFileIssuerPublicKey), ipk) | ||
writeFile(filepath.Join(msp.IdemixConfigDirMsp, msp.IdemixConfigFileIssuerPublicKey), ipk) | ||
|
||
case genSignerConfig.FullCommand(): | ||
config, err := idemixca.GenerateSignerConfig(*genCredIsAdmin, *genCredOU, readIssuerKey()) | ||
handleError(err) | ||
|
||
path := msp.IdemixConfigDirUser | ||
checkDirectoryNotExists(path, fmt.Sprintf("This MSP config already contains a directory \"%s\"", path)) | ||
|
||
// Write config to file | ||
handleError(os.Mkdir(msp.IdemixConfigDirUser, 0770)) | ||
writeFile(filepath.Join(msp.IdemixConfigDirUser, msp.IdemixConfigFileSigner), config) | ||
|
||
case version.FullCommand(): | ||
printVersion() | ||
} | ||
} | ||
|
||
func printVersion() { | ||
fmt.Println(metadata.GetVersionInfo()) | ||
} | ||
|
||
// writeFile writes bytes to a file and panics in case of an error | ||
func writeFile(path string, contents []byte) { | ||
handleError(ioutil.WriteFile(path, contents, 0640)) | ||
} | ||
|
||
// readIssuerKey reads the issuer key from the current directory | ||
func readIssuerKey() *idemix.IssuerKey { | ||
path := filepath.Join(IdemixDirIssuer, IdemixConfigIssuerSecretKey) | ||
isk, err := ioutil.ReadFile(path) | ||
if err != nil { | ||
handleError(errors.Wrapf(err, "failed to open issuer secret key file: %s", path)) | ||
} | ||
path = filepath.Join(IdemixDirIssuer, msp.IdemixConfigFileIssuerPublicKey) | ||
ipkBytes, err := ioutil.ReadFile(path) | ||
if err != nil { | ||
handleError(errors.Wrapf(err, "failed to open issuer public key file: %s", path)) | ||
} | ||
ipk := &idemix.IssuerPublicKey{} | ||
handleError(proto.Unmarshal(ipkBytes, ipk)) | ||
key := &idemix.IssuerKey{isk, ipk} | ||
|
||
return key | ||
} | ||
|
||
// checkDirectoryNotExists checks whether a directory with the given path already exists and exits if this is the case | ||
func checkDirectoryNotExists(path string, errorMessage string) { | ||
_, err := os.Stat(path) | ||
if err == nil { | ||
handleError(errors.New(errorMessage)) | ||
} | ||
} | ||
|
||
func handleError(err error) { | ||
if err != nil { | ||
fmt.Println(err) | ||
os.Exit(1) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
Identity Mixer MSP configuration generator (idemixgen) | ||
====================================================== | ||
|
||
This document describes the usage for the idemixgen utility, which can be | ||
used to create configuration files for the identity mixer based MSP. | ||
Two commands are available, one for creating a fresh CA key pair, and one | ||
for creating an MSP config using a previously generated CA key. | ||
|
||
Directory Structure | ||
------------------- | ||
The idemixgen tool will create directories with the following structure: | ||
|
||
:: | ||
|
||
- /ca/ | ||
IssuerSecretKey | ||
IssuerPublicKey | ||
- /msp/ | ||
IssuerPublicKey | ||
- /user/ | ||
SignerConfig | ||
|
||
The ``ca`` directory contains the issuer secret key and should only be present | ||
for a CA. The ``msp`` directory contains the information required to set up an | ||
MSP verifying idemix signatures. The ``user`` directory specifies a default | ||
signer. | ||
|
||
CA Key Generation | ||
----------------- | ||
CA (issuer) keys suitable for identity mixer can be created using command | ||
``idemixgen ca-keygen``. This will create directories ``ca`` and ``msp`` in the | ||
working directory. | ||
|
||
Adding a Default Signer | ||
----------------------- | ||
After generating the ``ca`` and ``msp`` directories with | ||
``idemixgen ca-keygen``, a default signer specified in the ``user`` directory | ||
can be added to the config with ``idemixgen signerconfig``. | ||
|
||
:: | ||
|
||
$ idemixgen signerconfig -h | ||
usage: idemixgen signerconfig [<flags>] | ||
|
||
Generate a default signer for this Idemix MSP | ||
|
||
Flags: | ||
-h, --help Show context-sensitive help (also try --help-long and --help-man). | ||
-u, --org-unit=ORG-UNIT The Organizational Unit of the default signer | ||
-a, --admin Make the default signer admin | ||
|
||
For example, we can create a default signer that is a member of organizational | ||
unit "OrgUnit1" and that is an admin with the following command: | ||
:: | ||
|
||
idemixgen signerconfig -u OrgUnit1 --admin | ||
|
Oops, something went wrong.