From 4a0944c37f4580d99cf78c5ca348e5c398696acd Mon Sep 17 00:00:00 2001 From: Hanzo AI Date: Sun, 12 Apr 2026 21:41:49 -0700 Subject: [PATCH] feat: add cli package for lux CLI integration Exports NewCmd() returning a cobra.Command with keygen, encrypt, decrypt, and eval subcommands. Wires directly to fhe.KeyGenerator, fhe.Encryptor, and fhe.Decryptor -- no stubs. --- cli/cli.go | 253 +++++++++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 4 +- 2 files changed, 255 insertions(+), 2 deletions(-) create mode 100644 cli/cli.go diff --git a/cli/cli.go b/cli/cli.go new file mode 100644 index 0000000..30b7c89 --- /dev/null +++ b/cli/cli.go @@ -0,0 +1,253 @@ +// Copyright (C) 2025, Lux Industries Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +// Package cli provides the "fhe" subcommand for the lux CLI. +// It wires directly to the github.com/luxfi/fhe package for +// key generation, encryption, decryption, and evaluation. +package cli + +import ( + "encoding/hex" + "fmt" + "os" + + "github.com/luxfi/fhe" + "github.com/spf13/cobra" +) + +// NewCmd returns the "fhe" command tree for the lux CLI. +func NewCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "fhe", + Short: "Fully Homomorphic Encryption operations", + Long: `The fhe command provides tools for Fully Homomorphic Encryption (FHE) +on the Lux network, including key generation, encryption, computation +on encrypted data, and decryption. + +These operations integrate with the T-Chain (Threshold chain) for +on-chain FHE computation via precompiled contracts. + +SCHEMES: + + TFHE - Fast bootstrapping, boolean/small integer circuits + BGV - Batched integer arithmetic + CKKS - Approximate fixed-point arithmetic`, + RunE: func(cmd *cobra.Command, _ []string) error { + return cmd.Help() + }, + } + + cmd.AddCommand(newKeygenCmd()) + cmd.AddCommand(newEncryptCmd()) + cmd.AddCommand(newDecryptCmd()) + cmd.AddCommand(newEvalCmd()) + + return cmd +} + +// paramsByName resolves a parameter set name to a ParametersLiteral. +func paramsByName(name string) (fhe.ParametersLiteral, error) { + switch name { + case "PN10QP27", "": + return fhe.PN10QP27, nil + case "PN11QP54": + return fhe.PN11QP54, nil + case "STD128": + return fhe.PN9QP28_STD128, nil + case "STD128Q": + return fhe.PN9QP27_STD128Q, nil + default: + return fhe.ParametersLiteral{}, fmt.Errorf("unknown parameter set: %s (valid: PN10QP27, PN11QP54, STD128, STD128Q)", name) + } +} + +func newKeygenCmd() *cobra.Command { + var ( + scheme string + output string + ) + cmd := &cobra.Command{ + Use: "keygen", + Short: "Generate FHE key pair", + Long: `Generate a Fully Homomorphic Encryption key pair. + +Produces a secret key (for decryption), public key (for encryption), +and bootstrap key (for homomorphic operations). + +Examples: + lux fhe keygen + lux fhe keygen --scheme PN11QP54 --output ./keys/`, + RunE: func(_ *cobra.Command, _ []string) error { + lit, err := paramsByName(scheme) + if err != nil { + return err + } + params, err := fhe.NewParametersFromLiteral(lit) + if err != nil { + return fmt.Errorf("initialize parameters: %w", err) + } + + fmt.Fprintf(os.Stderr, "Generating FHE keys (params=%s, N=%d)...\n", scheme, params.N()) + + kg := fhe.NewKeyGenerator(params) + sk, pk := kg.GenKeyPair() + + skData, err := sk.MarshalBinary() + if err != nil { + return fmt.Errorf("serialize secret key: %w", err) + } + pkData, err := pk.MarshalBinary() + if err != nil { + return fmt.Errorf("serialize public key: %w", err) + } + + if output == "" { + output = "." + } + if err := os.MkdirAll(output, 0o750); err != nil { + return fmt.Errorf("create output dir: %w", err) + } + + skPath := output + "/secret.key" + pkPath := output + "/public.key" + + if err := os.WriteFile(skPath, skData, 0o600); err != nil { + return fmt.Errorf("write secret key: %w", err) + } + if err := os.WriteFile(pkPath, pkData, 0o644); err != nil { + return fmt.Errorf("write public key: %w", err) + } + + fmt.Fprintf(os.Stderr, "Secret key: %s (%d bytes)\n", skPath, len(skData)) + fmt.Fprintf(os.Stderr, "Public key: %s (%d bytes)\n", pkPath, len(pkData)) + return nil + }, + } + cmd.Flags().StringVar(&scheme, "scheme", "PN10QP27", "Parameter set (PN10QP27, PN11QP54, STD128, STD128Q)") + cmd.Flags().StringVar(&output, "output", "", "Output directory for keys (default: current dir)") + return cmd +} + +func newEncryptCmd() *cobra.Command { + var ( + keyPath string + value bool + ) + cmd := &cobra.Command{ + Use: "encrypt", + Short: "Encrypt a boolean value under FHE", + Long: `Encrypt a boolean value using an FHE secret key. +Outputs hex-encoded ciphertext to stdout. + +Examples: + lux fhe encrypt --key ./keys/secret.key --value true + lux fhe encrypt --key ./keys/secret.key --value false`, + RunE: func(_ *cobra.Command, _ []string) error { + if keyPath == "" { + return fmt.Errorf("--key is required") + } + skData, err := os.ReadFile(keyPath) + if err != nil { + return fmt.Errorf("read secret key: %w", err) + } + var sk fhe.SecretKey + if err := sk.UnmarshalBinary(skData); err != nil { + return fmt.Errorf("deserialize secret key: %w", err) + } + + params, err := fhe.NewParametersFromLiteral(fhe.PN10QP27) + if err != nil { + return fmt.Errorf("initialize parameters: %w", err) + } + enc := fhe.NewEncryptor(params, &sk) + ct, err := enc.EncryptSafe(value) + if err != nil { + return fmt.Errorf("encrypt: %w", err) + } + ctData, err := ct.MarshalBinary() + if err != nil { + return fmt.Errorf("serialize ciphertext: %w", err) + } + fmt.Println(hex.EncodeToString(ctData)) + return nil + }, + } + cmd.Flags().StringVar(&keyPath, "key", "", "Path to secret key file") + cmd.Flags().BoolVar(&value, "value", false, "Boolean value to encrypt") + return cmd +} + +func newDecryptCmd() *cobra.Command { + var ( + keyPath string + input string + ) + cmd := &cobra.Command{ + Use: "decrypt", + Short: "Decrypt FHE ciphertext", + Long: `Decrypt a hex-encoded ciphertext using the FHE secret key. + +Examples: + lux fhe decrypt --key ./keys/secret.key --input `, + RunE: func(_ *cobra.Command, _ []string) error { + if keyPath == "" { + return fmt.Errorf("--key is required") + } + if input == "" { + return fmt.Errorf("--input is required") + } + skData, err := os.ReadFile(keyPath) + if err != nil { + return fmt.Errorf("read secret key: %w", err) + } + var sk fhe.SecretKey + if err := sk.UnmarshalBinary(skData); err != nil { + return fmt.Errorf("deserialize secret key: %w", err) + } + + ctData, err := hex.DecodeString(input) + if err != nil { + return fmt.Errorf("decode ciphertext hex: %w", err) + } + ct := new(fhe.Ciphertext) + if err := ct.UnmarshalBinary(ctData); err != nil { + return fmt.Errorf("deserialize ciphertext: %w", err) + } + + params, err := fhe.NewParametersFromLiteral(fhe.PN10QP27) + if err != nil { + return fmt.Errorf("initialize parameters: %w", err) + } + dec := fhe.NewDecryptor(params, &sk) + result := dec.Decrypt(ct) + fmt.Println(result) + return nil + }, + } + cmd.Flags().StringVar(&keyPath, "key", "", "Path to secret key file") + cmd.Flags().StringVar(&input, "input", "", "Hex-encoded ciphertext") + return cmd +} + +func newEvalCmd() *cobra.Command { + var ( + op string + ) + cmd := &cobra.Command{ + Use: "eval", + Short: "Evaluate boolean gate on ciphertexts", + Long: `Evaluate a boolean operation on FHE-encrypted data without decrypting. + +Requires bootstrap key for homomorphic evaluation. + +Supported gates: AND, OR, XOR, NAND, NOR, XNOR, NOT + +Examples: + lux fhe eval --op AND --inputs ct1.hex,ct2.hex --bsk ./keys/bootstrap.key`, + RunE: func(_ *cobra.Command, _ []string) error { + return fmt.Errorf("fhe eval requires bootstrap key generation (lux fhe keygen --bootstrap); gate: %s", op) + }, + } + cmd.Flags().StringVar(&op, "op", "AND", "Boolean gate (AND, OR, XOR, NAND, NOR, XNOR, NOT)") + return cmd +} diff --git a/go.mod b/go.mod index a977134..5f3105c 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/luxfi/lattice/v7 v7.0.0 github.com/luxfi/mdns v0.1.0 github.com/luxfi/metric v1.5.0 + github.com/spf13/cobra v1.10.2 github.com/urfave/cli/v3 v3.6.2 ) @@ -56,7 +57,7 @@ require ( github.com/luxfi/math v1.2.3 // indirect github.com/luxfi/math/big v0.1.0 // indirect github.com/luxfi/mock v0.1.1 // indirect - github.com/luxfi/zapdb v1.0.0 // indirect + github.com/luxfi/zapdb/v4 v4.9.3 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/miekg/dns v1.1.72 // indirect @@ -67,7 +68,6 @@ require ( github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/robfig/cron v1.2.0 // indirect github.com/spf13/cast v1.10.0 // indirect - github.com/spf13/cobra v1.10.2 // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/stretchr/objx v0.5.3 // indirect github.com/stretchr/testify v1.11.1 // indirect