170 lines
3.7 KiB
Go
170 lines
3.7 KiB
Go
package identity
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io/ioutil"
|
|
|
|
logging "github.com/ipfs/go-log"
|
|
crypto "github.com/libp2p/go-libp2p-crypto"
|
|
peer "github.com/libp2p/go-libp2p-peer"
|
|
)
|
|
|
|
var logger = logging.Logger("identity")
|
|
|
|
// Identity defaults
|
|
const (
|
|
DefaultConfigCrypto = crypto.RSA
|
|
DefaultConfigKeyLength = 2048
|
|
)
|
|
|
|
// Identity represents identity of a cluster peer for communication,
|
|
// including the Consensus component.
|
|
type Identity struct {
|
|
ID peer.ID
|
|
PrivateKey crypto.PrivKey
|
|
}
|
|
|
|
// identityJSON represents a Cluster peer identity as it will look when it is
|
|
// saved using JSON.
|
|
type identityJSON struct {
|
|
ID string `json:"id"`
|
|
PrivateKey string `json:"private_key"`
|
|
}
|
|
|
|
// New generate a public-private keypair and returns a new Identity.
|
|
func New() (*Identity, error) {
|
|
// pid and private key generation --
|
|
priv, pub, err := crypto.GenerateKeyPair(
|
|
DefaultConfigCrypto,
|
|
DefaultConfigKeyLength)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pid, err := peer.IDFromPublicKey(pub)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &Identity{
|
|
ID: pid,
|
|
PrivateKey: priv,
|
|
}, nil
|
|
}
|
|
|
|
// SaveJSON saves the JSON representation of the Identity to
|
|
// the given path.
|
|
func (ident *Identity) SaveJSON(path string) error {
|
|
logger.Info("Saving configuration")
|
|
|
|
bs, err := ident.ToJSON()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return ioutil.WriteFile(path, bs, 0600)
|
|
}
|
|
|
|
// ToJSON generates a human-friendly version of Identity.
|
|
func (ident *Identity) ToJSON() (raw []byte, err error) {
|
|
jID, err := ident.toIdentityJSON()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
raw, err = json.MarshalIndent(jID, "", " ")
|
|
return
|
|
}
|
|
|
|
func (ident *Identity) toIdentityJSON() (jID *identityJSON, err error) {
|
|
// Multiaddress String() may panic
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
err = fmt.Errorf("%s", r)
|
|
}
|
|
}()
|
|
|
|
jID = &identityJSON{}
|
|
|
|
// Private Key
|
|
pkeyBytes, err := ident.PrivateKey.Bytes()
|
|
if err != nil {
|
|
return
|
|
}
|
|
pKey := base64.StdEncoding.EncodeToString(pkeyBytes)
|
|
|
|
// Set all identity fields
|
|
jID.ID = ident.ID.Pretty()
|
|
jID.PrivateKey = pKey
|
|
return
|
|
}
|
|
|
|
// LoadJSON receives a raw json-formatted identity and
|
|
// sets the Config fields from it. Note that it should be JSON
|
|
// as generated by ToJSON().
|
|
func LoadJSON(raw []byte) (*Identity, error) {
|
|
jID := &identityJSON{}
|
|
err := json.Unmarshal(raw, jID)
|
|
if err != nil {
|
|
logger.Error("Error unmarshaling cluster config")
|
|
return nil, err
|
|
}
|
|
|
|
return applyConfigJSON(jID)
|
|
}
|
|
|
|
func applyConfigJSON(jID *identityJSON) (*Identity, error) {
|
|
ident := Identity{}
|
|
pid, err := peer.IDB58Decode(jID.ID)
|
|
if err != nil {
|
|
err = fmt.Errorf("error decoding cluster ID: %s", err)
|
|
return nil, err
|
|
}
|
|
ident.ID = pid
|
|
|
|
pkb, err := base64.StdEncoding.DecodeString(jID.PrivateKey)
|
|
if err != nil {
|
|
err = fmt.Errorf("error decoding private_key: %s", err)
|
|
return nil, err
|
|
}
|
|
pKey, err := crypto.UnmarshalPrivateKey(pkb)
|
|
if err != nil {
|
|
err = fmt.Errorf("error parsing private_key ID: %s", err)
|
|
return nil, err
|
|
}
|
|
ident.PrivateKey = pKey
|
|
|
|
return &ident, ident.Validate()
|
|
}
|
|
|
|
// Validate will check that the values of this identity
|
|
// seem to be working ones.
|
|
func (ident *Identity) Validate() error {
|
|
if ident.ID == "" {
|
|
return errors.New("identity ID not set")
|
|
}
|
|
|
|
if ident.PrivateKey == nil {
|
|
return errors.New("no identity private_key set")
|
|
}
|
|
|
|
if !ident.ID.MatchesPrivateKey(ident.PrivateKey) {
|
|
return errors.New("identity ID does not match the private_key")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// LoadJSONFromFile reads an Identity file from disk and parses
|
|
// it and return Identity.
|
|
func LoadJSONFromFile(path string) (*Identity, error) {
|
|
file, err := ioutil.ReadFile(path)
|
|
if err != nil {
|
|
logger.Error("error reading the configuration file: ", err)
|
|
return nil, err
|
|
}
|
|
|
|
return LoadJSON(file)
|
|
}
|