Files

123 lines
3.1 KiB
Go

package main
import (
"fmt"
"io"
"os"
"github.com/spf13/cobra"
"git.tswf.io/infra/go-synapse-backupper/pkg/adapters/crypto/composite"
"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"
)
var (
newRestoreKeyManager = keymanager.NewKeyManager
newRestoreDecryptor = func(registry crypto.Registry) crypto.Decryptor {
return composite.NewDecryptor(registry)
}
restoreOutput io.Writer = os.Stdout
restoreOsOpen = os.Open
)
var restoreCmd = &cobra.Command{
Use: "restore",
Short: "Restore a backup from a .pqenc file",
PreRunE: func(cmd *cobra.Command, args []string) error {
privkeyPq, _ := cmd.Flags().GetString("privkey-pq")
privkeyClassical, _ := cmd.Flags().GetString("privkey-classical")
var pqMissing bool
if _, err := os.Stat(privkeyPq); err != nil {
pqMissing = true
}
var classicalMissing bool
if _, err := os.Stat(privkeyClassical); err != nil {
classicalMissing = true
}
if pqMissing || classicalMissing {
return fmt.Errorf(
"--privkey-pq and --privkey-classical are both required (AND model)",
)
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
inPath, _ := cmd.Flags().GetString("in")
outPath, _ := cmd.Flags().GetString("out")
privkeyPq, _ := cmd.Flags().GetString("privkey-pq")
privkeyClassical, _ := cmd.Flags().GetString("privkey-classical")
inFile, err := restoreOsOpen(inPath)
if err != nil {
return fmt.Errorf("open input file: %w", err)
}
defer inFile.Close()
registry := crypto.NewRegistry()
_ = registry.Register(
0x0006,
func() crypto.KEM { return mlkem768.New() },
)
_ = registry.Register(
0x0007,
func() crypto.KEM { return x25519.New() },
)
keyManager := newRestoreKeyManager(registry)
pqPriv, err := keyManager.LoadPriv(privkeyPq, 0x0006)
if err != nil {
return fmt.Errorf("load PQ private key: %w", err)
}
classicalPriv, err := keyManager.LoadPriv(privkeyClassical, 0x0007)
if err != nil {
return fmt.Errorf("load classical private key: %w", err)
}
var out io.Writer
if outPath != "" {
outFile, err := os.Create(outPath)
if err != nil {
return fmt.Errorf("create output file: %w", err)
}
defer outFile.Close()
out = outFile
} else {
out = restoreOutput
}
decryptor := newRestoreDecryptor(registry)
if err := decryptor.Decrypt(
inFile,
[]crypto.RecipientPriv{pqPriv, classicalPriv},
out,
); err != nil {
return err
}
return nil
},
}
func init() {
restoreCmd.Flags().String("in", "", "Input .pqenc file path")
restoreCmd.Flags().String("privkey-pq", "", "Path to PQ private key PEM")
restoreCmd.Flags().String(
"privkey-classical",
"",
"Path to classical private key PEM",
)
restoreCmd.Flags().String("out", "", "Output file path (empty = stdout)")
_ = restoreCmd.MarkFlagRequired("in")
_ = restoreCmd.MarkFlagRequired("privkey-pq")
_ = restoreCmd.MarkFlagRequired("privkey-classical")
}