Skip to content

Commit

Permalink
Merge pull request #1148 from bloxapp/version/main_keygen_fix
Browse files Browse the repository at this point in the history
main keygen fix
  • Loading branch information
Lior Rutenberg authored Sep 20, 2023
2 parents 08bc3e9 + 3bb8d56 commit 71dbd49
Show file tree
Hide file tree
Showing 10 changed files with 134 additions and 103 deletions.
1 change: 1 addition & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -176,3 +176,4 @@ Deploy exporter to prod:

only:
- main

12 changes: 11 additions & 1 deletion cli/bootnode/boot_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,17 @@ var StartBootNodeCmd = &cobra.Command{
log.Fatal(err)
}

if err := logging.SetGlobalLogger(cfg.LogLevel, cfg.LogLevelFormat, cfg.LogFormat, cfg.LogFilePath); err != nil {
err := logging.SetGlobalLogger(
cfg.LogLevel,
cfg.LogLevelFormat,
cfg.LogFormat,
&logging.LogFileOptions{
FileName: cfg.LogFilePath,
MaxSize: cfg.LogFileSize,
MaxBackups: cfg.LogFileBackups,
},
)
if err != nil {
log.Fatal(err)
}

Expand Down
2 changes: 2 additions & 0 deletions cli/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ type GlobalConfig struct {
LogFormat string `yaml:"LogFormat" env:"LOG_FORMAT" env-default:"console" env-description:"Defines logger's encoding, valid values are 'json' (default) and 'console''"`
LogLevelFormat string `yaml:"LogLevelFormat" env:"LOG_LEVEL_FORMAT" env-default:"capitalColor" env-description:"Defines logger's level format, valid values are 'capitalColor' (default), 'capital' or 'lowercase''"`
LogFilePath string `yaml:"LogFilePath" env:"LOG_FILE_PATH" env-default:"./data/debug.log" env-description:"Defines a file path to write logs into"`
LogFileSize int `yaml:"LogFileSize" env:"LOG_FILE_SIZE" env-default:"500" env-description:"Defines a file size in megabytes to rotate logs"`
LogFileBackups int `yaml:"LogFileBackups" env:"LOG_FILE_BACKUPS" env-default:"3" env-description:"Defines a number of backups to keep when rotating logs"`
}

// ProcessArgs processes and handles CLI arguments
Expand Down
2 changes: 1 addition & 1 deletion cli/export_keys_from_mnemonic.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ var exportKeysCmd = &cobra.Command{
Use: "export-keys",
Short: "exports private/public keys based on given mnemonic",
Run: func(cmd *cobra.Command, args []string) {
if err := logging.SetGlobalLogger("dpanic", "capital", "console", ""); err != nil {
if err := logging.SetGlobalLogger("dpanic", "capital", "console", nil); err != nil {
log.Fatal(err)
}

Expand Down
151 changes: 74 additions & 77 deletions cli/generate_operator_keys.go
Original file line number Diff line number Diff line change
@@ -1,122 +1,119 @@
package cli

import (
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4"

"log"
"os"
"path/filepath"

"github.com/bloxapp/ssv/logging"
"github.com/bloxapp/ssv/utils/rsaencryption"
"github.com/spf13/cobra"
"github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4"
"go.uber.org/zap"
)

// generateOperatorKeysCmd is the command to generate operator private/public keys
var generateOperatorKeysCmd = &cobra.Command{
Use: "generate-operator-keys",
Short: "generates ssv operator keys",
Run: func(cmd *cobra.Command, args []string) {
logger := zap.L().Named(RootCmd.Short)
if err := logging.SetGlobalLogger("debug", "capital", "console", nil); err != nil {
log.Fatal(err)
}
logger := zap.L().Named(logging.NameExportKeys)
passwordFilePath, _ := cmd.Flags().GetString("password-file")
privateKeyFilePath, _ := cmd.Flags().GetString("operator-key-file")
pk, sk, err := rsaencryption.GenerateKeys()
if err != nil && privateKeyFilePath == "" {
logger.Fatal("Failed to create key and operator key wasn't provided", zap.Error(err))
}

// Resolve to absolute path
passwordAbsPath, err := filepath.Abs(passwordFilePath)
if err != nil {
logger.Fatal("Failed to read absolute path of password file", zap.Error(err))
}

// Now read the file
// #nosec G304
passwordBytes, err := os.ReadFile(passwordAbsPath)
pk, sk, err := rsaencryption.GenerateKeys()
if err != nil {
logger.Fatal("Failed to read password file", zap.Error(err))
logger.Fatal("Failed to generate keys", zap.Error(err))
}

encryptionPassword := string(passwordBytes)

if privateKeyFilePath != "" {
// Resolve to absolute path
privateKeyAbsPath, err := filepath.Abs(privateKeyFilePath)
keyBytes, err := readFile(privateKeyFilePath)
if err != nil {
logger.Fatal("Failed to read absolute path of private key file", zap.Error(err))
logger.Fatal("Failed to read private key from file", zap.Error(err))
}

// Now read the file
// #nosec G304
privateKeyBytes, _ := os.ReadFile(privateKeyAbsPath)
if privateKeyBytes != nil {
keyBytes, err := base64.StdEncoding.DecodeString(string(privateKeyBytes))
if err != nil {
logger.Fatal("base64 decoding failed", zap.Error(err))
}

keyPem, _ := pem.Decode(keyBytes)
if keyPem == nil {
logger.Fatal("failed to decode PEM", zap.Error(err))
}

rsaKey, err := x509.ParsePKCS1PrivateKey(keyPem.Bytes)
if err != nil {
logger.Fatal("failed to parse RSA private key", zap.Error(err))
}

skPem := pem.EncodeToMemory(
&pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(rsaKey),
},
)

operatorPublicKey, _ := rsaencryption.ExtractPublicKey(rsaKey)
publicKey, _ := base64.StdEncoding.DecodeString(operatorPublicKey)
sk = skPem
pk = publicKey
sk, pk, err = parsePrivateKey(keyBytes)
if err != nil {
logger.Fatal("Failed to read private key from file", zap.Error(err))
}
}

if err := logging.SetGlobalLogger("debug", "capital", "console", ""); err != nil {
logger.Fatal("", zap.Error(err))
}

if err != nil {
logger.Fatal("Failed to generate operator keys", zap.Error(err))
}
logger.Info("generated public key (base64)", zap.String("pk", base64.StdEncoding.EncodeToString(pk)))

if encryptionPassword != "" {
encryptedData, err := keystorev4.New().Encrypt(sk, encryptionPassword)
if passwordFilePath != "" {
passwordBytes, err := readFile(passwordFilePath)
if err != nil {
logger.Fatal("Failed to encrypt private key", zap.Error(err))
logger.Fatal("Failed to read password file", zap.Error(err))
}

encryptedJSON, err := json.Marshal(encryptedData)
if err != nil {
logger.Fatal("Failed to marshal encrypted data to JSON", zap.Error(err))
encryptedJSON, encryptedJSONErr := encryptPrivateKey(sk, pk, passwordBytes)
if encryptedJSONErr != nil {
logger.Fatal("Failed to encrypt private key", zap.Error(err))
}

err = os.WriteFile("encrypted_private_key.json", encryptedJSON, 0600)
err = writeFile("encrypted_private_key.json", encryptedJSON)
if err != nil {
logger.Fatal("Failed to write encrypted private key to file", zap.Error(err))
logger.Fatal("Failed to save private key", zap.Error(err))
} else {
logger.Info("private key encrypted and stored in encrypted_private_key.json")
}

logger.Info("private key encrypted and stored in encrypted_private_key.json")
} else {
logger.Info("generated public key (base64)", zap.String("pk", base64.StdEncoding.EncodeToString(pk)))
logger.Info("generated private key (base64)", zap.String("sk", base64.StdEncoding.EncodeToString(sk)))
}
},
}

func parsePrivateKey(keyBytes []byte) ([]byte, []byte, error) {
decodedBytes, err := base64.StdEncoding.DecodeString(string(keyBytes))
if err != nil {
return nil, nil, err
}
rsaKey, err := rsaencryption.ConvertPemToPrivateKey(string(decodedBytes))
if err != nil {
return nil, nil, err
}

skPem := rsaencryption.PrivateKeyToByte(rsaKey)

operatorPublicKey, err := rsaencryption.ExtractPublicKey(rsaKey)
if err != nil {
return nil, nil, err
}
pk, err := base64.StdEncoding.DecodeString(operatorPublicKey)
if err != nil {
return nil, nil, err
}
return skPem, pk, nil
}

func encryptPrivateKey(sk []byte, pk []byte, passwordBytes []byte) ([]byte, error) {
encryptionPassword := string(passwordBytes)
encryptedData, err := keystorev4.New().Encrypt(sk, encryptionPassword)
if err != nil {
return nil, err
}
encryptedData["publicKey"] = base64.StdEncoding.EncodeToString(pk)
encryptedJSON, err := json.Marshal(encryptedData)
if err != nil {
return nil, err
}
return encryptedJSON, nil
}

func writeFile(fileName string, data []byte) error {
return os.WriteFile(fileName, data, 0600)
}

func readFile(filePath string) ([]byte, error) {
absPath, err := filepath.Abs(filePath)
if err != nil {
return nil, err
}
// #nosec G304
contentBytes, err := os.ReadFile(absPath)
return contentBytes, err
}

func init() {
generateOperatorKeysCmd.Flags().StringP("password-file", "p", "", "File path to the password used to encrypt the private key")
generateOperatorKeysCmd.Flags().StringP("operator-key-file", "o", "", "File path to the operator private key")
Expand Down
12 changes: 11 additions & 1 deletion cli/operator/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,17 @@ func setupGlobal(cmd *cobra.Command) (*zap.Logger, error) {
}
}

if err := logging.SetGlobalLogger(cfg.LogLevel, cfg.LogLevelFormat, cfg.LogFormat, cfg.LogFilePath); err != nil {
err := logging.SetGlobalLogger(
cfg.LogLevel,
cfg.LogLevelFormat,
cfg.LogFormat,
&logging.LogFileOptions{
FileName: cfg.LogFilePath,
MaxSize: cfg.LogFileSize,
MaxBackups: cfg.LogFileBackups,
},
)
if err != nil {
return nil, fmt.Errorf("logging.SetGlobalLogger: %w", err)
}

Expand Down
2 changes: 1 addition & 1 deletion cli/threshold.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ var createThresholdCmd = &cobra.Command{
Use: "create-threshold",
Short: "Turns a private key into a threshold key",
Run: func(cmd *cobra.Command, args []string) {
if err := logging.SetGlobalLogger("debug", "capital", "console", ""); err != nil {
if err := logging.SetGlobalLogger("debug", "capital", "console", nil); err != nil {
log.Fatal(err)
}
logger := zap.L().Named(logging.NameCreateThreshold)
Expand Down
2 changes: 1 addition & 1 deletion integration/qbft/tests/setup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func GetSharedData(t *testing.T) SharedData { //singleton B-)

func TestMain(m *testing.M) {
ctx := context.Background()
if err := logging.SetGlobalLogger("debug", "capital", "console", ""); err != nil {
if err := logging.SetGlobalLogger("debug", "capital", "console", nil); err != nil {
panic(err)
}

Expand Down
49 changes: 30 additions & 19 deletions logging/global.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,6 @@ import (
"go.uber.org/zap/zapcore"
)

// TODO: Log rotation out of the app
func getFileWriter(logFileName string) io.Writer {
fileLogger := &lumberjack.Logger{
Filename: logFileName,
MaxSize: 500, // megabytes
MaxBackups: 3,
MaxAge: 28, // days
Compress: false,
}

return fileLogger
}

func parseConfigLevel(levelName string) (zapcore.Level, error) {
return zapcore.ParseLevel(levelName)
}
Expand All @@ -43,7 +30,17 @@ func parseConfigLevelEncoder(levelEncoderName string) zapcore.LevelEncoder {
}
}

func SetGlobalLogger(levelName string, levelEncoderName string, logFormat string, logFilePath string) error {
func SetGlobalLogger(levelName string, levelEncoderName string, logFormat string, fileOptions *LogFileOptions) (err error) {
defer func() {
if err == nil {
zap.L().Debug("logger is ready",
zap.String("level", levelName),
zap.String("encoder", levelEncoderName),
zap.String("format", logFormat),
zap.Any("file_options", fileOptions),
)
}
}()
level, err := parseConfigLevel(levelName)
if err != nil {
return err
Expand Down Expand Up @@ -77,25 +74,39 @@ func SetGlobalLogger(levelName string, levelEncoderName string, logFormat string

consoleCore := zapcore.NewCore(zapcore.NewConsoleEncoder(cfg.EncoderConfig), os.Stdout, lv)

if logFilePath == "" {
if fileOptions == nil {
zap.ReplaceGlobals(zap.New(consoleCore))
return nil
}

logFileWriter := getFileWriter(logFilePath)

lv2 := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
return true // debug log returns all logs
})

dev := zapcore.NewJSONEncoder(zap.NewDevelopmentEncoderConfig())
fileCore := zapcore.NewCore(dev, zapcore.AddSync(logFileWriter), lv2)
fileWriter := fileOptions.writer(fileOptions)
fileCore := zapcore.NewCore(dev, zapcore.AddSync(fileWriter), lv2)

zap.ReplaceGlobals(zap.New(zapcore.NewTee(consoleCore, fileCore)))

return nil
}

type LogFileOptions struct {
FileName string
MaxSize int
MaxBackups int
}

func (o LogFileOptions) writer(options *LogFileOptions) io.Writer {
return &lumberjack.Logger{
Filename: options.FileName,
MaxSize: options.MaxSize, // megabytes
MaxBackups: options.MaxBackups,
MaxAge: 28, // days
Compress: false,
}
}

func CapturePanic(logger *zap.Logger) {
if r := recover(); r != nil {
// defer logger.Sync()
Expand Down
4 changes: 2 additions & 2 deletions logging/testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import (
)

func TestLogger(t *testing.T) *zap.Logger {
err := SetGlobalLogger("debug", "capital", "console", "")
err := SetGlobalLogger("debug", "capital", "console", nil)
require.NoError(t, err)
return zap.L().Named(t.Name())
}

func BenchLogger(b *testing.B) *zap.Logger {
err := SetGlobalLogger("debug", "capital", "console", "")
err := SetGlobalLogger("debug", "capital", "console", nil)
require.NoError(b, err)
return zap.L().Named(b.Name())
}

0 comments on commit 71dbd49

Please sign in to comment.