3950dd94ee
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
131 lines
3.5 KiB
Go
131 lines
3.5 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"git.tswf.io/infra/go-synapse-backupper/pkg/adapters/crypto/keymanager"
|
|
"git.tswf.io/infra/go-synapse-backupper/pkg/adapters/crypto/mlkem768"
|
|
"git.tswf.io/infra/go-synapse-backupper/pkg/adapters/crypto/x25519"
|
|
"git.tswf.io/infra/go-synapse-backupper/pkg/domain/crypto"
|
|
)
|
|
|
|
func newRegistry() crypto.Registry {
|
|
reg := crypto.NewRegistry()
|
|
_ = reg.Register(0x0006, func() crypto.KEM { return mlkem768.New() })
|
|
_ = reg.Register(0x0007, func() crypto.KEM { return x25519.New() })
|
|
return reg
|
|
}
|
|
|
|
func newKeygenCmd() *cobra.Command {
|
|
return newKeygenCmdWithDeps(newRegistry())
|
|
}
|
|
|
|
func newKeygenCmdWithDeps(reg crypto.Registry) *cobra.Command {
|
|
var (
|
|
keyType string
|
|
outPrefix string
|
|
force bool
|
|
)
|
|
|
|
cmd := &cobra.Command{
|
|
Use: "keygen",
|
|
Short: "Generate encryption key pairs",
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
schemes := []struct {
|
|
name string
|
|
schemeID uint16
|
|
}{}
|
|
|
|
switch keyType {
|
|
case "pq":
|
|
schemes = append(schemes, struct {
|
|
name string
|
|
schemeID uint16
|
|
}{"pq", 0x0006})
|
|
case "classical":
|
|
schemes = append(schemes, struct {
|
|
name string
|
|
schemeID uint16
|
|
}{"classical", 0x0007})
|
|
case "both":
|
|
schemes = append(
|
|
schemes,
|
|
struct {
|
|
name string
|
|
schemeID uint16
|
|
}{"pq", 0x0006},
|
|
struct {
|
|
name string
|
|
schemeID uint16
|
|
}{"classical", 0x0007},
|
|
)
|
|
default:
|
|
return fmt.Errorf("invalid --type %q; must be pq, classical, or both", keyType)
|
|
}
|
|
|
|
if !force {
|
|
for _, scheme := range schemes {
|
|
pubPath := outPrefix + "." + scheme.name + ".pub.pem"
|
|
privPath := outPrefix + "." + scheme.name + ".priv.pem"
|
|
paths := make([]string, 0, 2)
|
|
paths = append(paths, pubPath, privPath)
|
|
for _, path := range paths {
|
|
if _, err := os.Stat(path); err == nil {
|
|
return fmt.Errorf("file already exists: %s (use --force to overwrite)", path)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
km := keymanager.NewKeyManager(reg)
|
|
|
|
for _, scheme := range schemes {
|
|
pubPath := outPrefix + "." + scheme.name + ".pub.pem"
|
|
privPath := outPrefix + "." + scheme.name + ".priv.pem"
|
|
|
|
pubFile, err := os.OpenFile(pubPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o644)
|
|
if err != nil {
|
|
return fmt.Errorf("create public key file %s: %w", pubPath, err)
|
|
}
|
|
|
|
privFile, err := os.OpenFile(privPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600)
|
|
if err != nil {
|
|
_ = pubFile.Close()
|
|
return fmt.Errorf("create private key file %s: %w", privPath, err)
|
|
}
|
|
|
|
if err := km.Generate(scheme.schemeID, pubFile, privFile, rand.Reader); err != nil {
|
|
_ = pubFile.Close()
|
|
_ = privFile.Close()
|
|
return fmt.Errorf("generate %s keys: %w", scheme.name, err)
|
|
}
|
|
|
|
if err := pubFile.Close(); err != nil {
|
|
return fmt.Errorf("close public key file %s: %w", pubPath, err)
|
|
}
|
|
if err := privFile.Close(); err != nil {
|
|
return fmt.Errorf("close private key file %s: %w", privPath, err)
|
|
}
|
|
if err := os.Chmod(pubPath, 0o644); err != nil {
|
|
return fmt.Errorf("chmod public key file %s: %w", pubPath, err)
|
|
}
|
|
if err := os.Chmod(privPath, 0o600); err != nil {
|
|
return fmt.Errorf("chmod private key file %s: %w", privPath, err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
},
|
|
}
|
|
|
|
cmd.Flags().StringVar(&keyType, "type", "both", "Key type to generate (pq|classical|both)")
|
|
cmd.Flags().StringVar(&outPrefix, "out-prefix", "", "Output file path prefix")
|
|
cmd.Flags().BoolVar(&force, "force", false, "Overwrite existing files")
|
|
|
|
return cmd
|
|
}
|