Skip to content

Commit

Permalink
lesson 8
Browse files Browse the repository at this point in the history
  • Loading branch information
bodrovis committed Jun 19, 2024
1 parent 010e090 commit 48878f7
Show file tree
Hide file tree
Showing 14 changed files with 4,533 additions and 0 deletions.
166 changes: 166 additions & 0 deletions lesson_8/cmd/keys/generate.go
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
}
16 changes: 16 additions & 0 deletions lesson_8/cmd/keys/keys.go
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)
}
36 changes: 36 additions & 0 deletions lesson_8/cmd/root.go
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
}
Loading

0 comments on commit 48878f7

Please sign in to comment.