-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
4,533 additions
and
0 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,166 @@ | ||
package keys | ||
|
||
import ( | ||
"crypto/rand" | ||
"crypto/rsa" | ||
"crypto/x509" | ||
"encoding/base64" | ||
"encoding/pem" | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
|
||
"brave_signer/config" | ||
"brave_signer/crypto_utils" | ||
"brave_signer/logger" | ||
"brave_signer/utils" | ||
|
||
"github.com/spf13/cobra" | ||
"github.com/spf13/viper" | ||
) | ||
|
||
type PrivateKeyGen struct { | ||
outputPath string | ||
keyBitSize int | ||
saltSize int | ||
} | ||
|
||
func init() { | ||
keysCmd.AddCommand(keysGenerateCmd) | ||
|
||
keysGenerateCmd.Flags().String("pub-key-path", "pub_key.pem", "Path to save the public key") | ||
keysGenerateCmd.Flags().String("priv-key-path", "priv_key.pem", "Path to save the private key") | ||
keysGenerateCmd.Flags().Int("priv-key-size", 2048, "Private key size in bits") | ||
keysGenerateCmd.Flags().Int("salt-size", 16, "Salt size used in key derivation in bytes") | ||
} | ||
|
||
var keysGenerateCmd = &cobra.Command{ | ||
Use: "generate", | ||
Short: "Generates key pair.", | ||
Long: `Generate an RSA key pair and store it in PEM files. The private key will be encrypted using a passphrase that you'll need to enter. AES encryption with Argon2 key derivation function is utilized.`, | ||
Run: func(cmd *cobra.Command, args []string) { | ||
localViper := cmd.Context().Value(config.ViperKey).(*viper.Viper) | ||
|
||
pkGenConfig := PrivateKeyGen{ | ||
outputPath: localViper.GetString("priv-key-path"), | ||
keyBitSize: localViper.GetInt("priv-key-size"), | ||
saltSize: localViper.GetInt("salt-size"), | ||
} | ||
|
||
privateKey, err := generatePrivKey(pkGenConfig) | ||
logger.HaltOnErr(err) | ||
|
||
err = generatePubKey(localViper.GetString("pub-key-path"), privateKey) | ||
logger.HaltOnErr(err) | ||
}, | ||
} | ||
|
||
func generatePubKey(path string, privKey *rsa.PrivateKey) error { | ||
absPath, err := filepath.Abs(path) | ||
if err != nil { | ||
return fmt.Errorf("failed to get absolute path: %v", err) | ||
} | ||
|
||
pubASN1, err := x509.MarshalPKIXPublicKey(&privKey.PublicKey) | ||
if err != nil { | ||
return fmt.Errorf("failed to marshal public key: %w", err) | ||
} | ||
|
||
file, err := os.Create(absPath) | ||
if err != nil { | ||
return fmt.Errorf("failed to create public key file: %w", err) | ||
} | ||
defer file.Close() | ||
|
||
if err := pem.Encode(file, &pem.Block{Type: "RSA PUBLIC KEY", Bytes: pubASN1}); err != nil { | ||
return fmt.Errorf("failed to encode public key to PEM: %w", err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func generatePrivKey(pkGenConfig PrivateKeyGen) (*rsa.PrivateKey, error) { | ||
absPath, err := filepath.Abs(pkGenConfig.outputPath) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to get absolute path: %v", err) | ||
} | ||
|
||
privateKey, err := rsa.GenerateKey(rand.Reader, pkGenConfig.keyBitSize) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to generate private key: %v", err) | ||
} | ||
|
||
passphrase, err := utils.GetPassphrase() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
privateKeyBytes := x509.MarshalPKCS1PrivateKey(privateKey) | ||
|
||
salt, err := makeSalt(pkGenConfig.saltSize) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
key, err := crypto_utils.DeriveKey(crypto_utils.KeyDerivationConfig{ | ||
Passphrase: passphrase, | ||
Salt: salt, | ||
}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
crypter, err := crypto_utils.MakeCrypter(key) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
nonce, err := crypto_utils.MakeNonce(crypter) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
encryptedData := crypter.Seal(nil, nonce, privateKeyBytes, nil) | ||
|
||
// Create a PEM block with the encrypted data | ||
encryptedPEMBlock := &pem.Block{ | ||
Type: "ENCRYPTED PRIVATE KEY", | ||
Bytes: encryptedData, | ||
Headers: map[string]string{ | ||
"Nonce": base64.StdEncoding.EncodeToString(nonce), | ||
"Salt": base64.StdEncoding.EncodeToString(salt), | ||
"Key-Derivation-Function": "Argon2", | ||
}, | ||
} | ||
|
||
err = savePrivKeyToPEM(absPath, encryptedPEMBlock) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return privateKey, nil | ||
} | ||
|
||
func savePrivKeyToPEM(absPath string, encryptedPEMBlock *pem.Block) error { | ||
privKeyFile, err := os.Create(absPath) | ||
if err != nil { | ||
return fmt.Errorf("failed to create private key file: %v", err) | ||
} | ||
defer privKeyFile.Close() | ||
|
||
if err := pem.Encode(privKeyFile, encryptedPEMBlock); err != nil { | ||
return fmt.Errorf("failed to encode private key to PEM: %w", err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// makeSalt generates a cryptographic salt. | ||
func makeSalt(saltSize int) ([]byte, error) { | ||
salt := make([]byte, saltSize) | ||
if _, err := rand.Read(salt); err != nil { | ||
return nil, fmt.Errorf("failed to generate salt: %v", err) | ||
} | ||
|
||
return salt, nil | ||
} |
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,16 @@ | ||
package keys | ||
|
||
import ( | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
var keysCmd = &cobra.Command{ | ||
Use: "keys", | ||
Short: "Manage key pairs.", | ||
Long: `Use subcommands to create public/private key pairs in PEM files.`, | ||
} | ||
|
||
// Init initializes keys commands | ||
func Init(rootCmd *cobra.Command) { | ||
rootCmd.AddCommand(keysCmd) | ||
} |
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,36 @@ | ||
package cmd | ||
|
||
import ( | ||
"context" | ||
|
||
"brave_signer/config" | ||
|
||
"github.com/spf13/cobra" | ||
) | ||
|
||
func RootCmd() *cobra.Command { | ||
return &cobra.Command{ | ||
Use: "brave_signer", | ||
Short: "Bravely generate key pairs, sign files, and check signatures.", | ||
Long: `A collection of tools to generate key pairs in PEM files, sign files, and verify signatures.`, | ||
SilenceUsage: true, | ||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error { | ||
return initializeConfig(cmd) | ||
}, | ||
} | ||
} | ||
|
||
func initializeConfig(cmd *cobra.Command) error { | ||
localViper, err := config.LoadYamlConfig() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if err := config.BindFlags(cmd, localViper); err != nil { | ||
return err | ||
} | ||
|
||
ctx := context.WithValue(cmd.Context(), config.ViperKey, localViper) | ||
cmd.SetContext(ctx) | ||
return nil | ||
} |
Oops, something went wrong.