package main import ( "crypto" "crypto/rand" "crypto/rsa" "crypto/sha1" "crypto/x509" "encoding/hex" "encoding/pem" "errors" "fmt" "log" "os" "os/signal" "path" "git.greysoh.dev/imterah/boron/bofs" "github.com/hanwen/go-fuse/v2/fs" "github.com/urfave/cli/v2" ) func initializeFS(cCtx *cli.Context) error { privKeyPath := cCtx.String("key") allowKeyGeneration := cCtx.Bool("allow-keygeneration") srcFolder := cCtx.String("source-folder") destFolder := cCtx.String("dest-folder") privKeyRaw, err := os.ReadFile(privKeyPath) var privateKey *rsa.PrivateKey if err != nil && errors.Is(err, os.ErrNotExist) && allowKeyGeneration { log.Println("key not found. generating key...") privateKey, err = rsa.GenerateKey(rand.Reader, 4096) if err != nil { return fmt.Errorf("could not generate private key: %s", err.Error()) } log.Println("getting key signature...") sha1Instance := sha1.New() keySignature := sha1Instance.Sum(nil) if _, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA1, keySignature); err != nil { return fmt.Errorf("could not get key fingerprint/signature: %s", err.Error()) } log.Println("saving public key...") if err := os.Mkdir(path.Join(srcFolder, "keys"), os.ModePerm); err != nil && !errors.Is(err, os.ErrExist) { return fmt.Errorf("failed to create keys folder: %s", err.Error()) } pubKeyFile, err := os.Create(path.Join(srcFolder, "keys", hex.EncodeToString(keySignature)+".pub")) if err != nil { return fmt.Errorf("failed to open public key file: %s", err.Error()) } // We're doing PCKS1 (todo: look into maybe changing to PCKS8?) err = pem.Encode( pubKeyFile, &pem.Block{ Type: "PUBLIC KEY", Bytes: x509.MarshalPKCS1PublicKey(&privateKey.PublicKey), }, ) if err != nil { return fmt.Errorf("failed to encode public key: %s", err.Error()) } pubKeyFile.Close() privKeyFile, err := os.Create(privKeyPath) if err != nil { return fmt.Errorf("failed to open public key file: %s", err.Error()) } log.Println("saving private key...") err = pem.Encode( privKeyFile, &pem.Block{ Type: "PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privateKey), }, ) if err != nil { return fmt.Errorf("failed to encode private key: %s", err.Error()) } privKeyFile.Close() log.Println("finished key generation steps.") } else if err == nil { privKeyPem, _ := pem.Decode(privKeyRaw) if privKeyPem.Type != "RSA PRIVATE KEY" && privKeyPem.Type != "PRIVATE KEY" { return fmt.Errorf("decoded private key's header does not match RSA private key signature") } var parsedKey interface{} if parsedKey, err = x509.ParsePKCS1PrivateKey(privKeyPem.Bytes); err != nil { log.Println("failed to decode private key using PCKS1 format. trying PCKS8 next") if parsedKey, err = x509.ParsePKCS8PrivateKey(privKeyPem.Bytes); err != nil { return fmt.Errorf("failed to decode private key using PCKS8 format") } } var ok bool // Hack to make Go not complain privateKey, ok = parsedKey.(*rsa.PrivateKey) if !ok { return fmt.Errorf("failed to set privateKey variable (failed typecast)") } } else { return fmt.Errorf("could not read private key: %s", err.Error()) } opts := &fs.Options{} log.Println("mounting filesystem...") server, err := fs.Mount(destFolder, &bofs.BoronInode{}, opts) if err != nil { return err } log.Println("successfully mounted filesystem.") c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) go func() { for _ = range c { log.Println("unmounting filesystem...") err := server.Unmount() if err != nil { log.Println("failed to unmount filesystem.") } os.Exit(0) } }() server.Wait() return nil } func main() { app := &cli.App{ Name: "boron", Usage: "a collaborateive end-to-end-encrypted meta-filesystem", Flags: []cli.Flag{ &cli.StringFlag{ Name: "key", Usage: "armored private key to use", Required: true, }, &cli.BoolFlag{ Name: "allow-keygeneration", Usage: "if true, allows keys to be generated if the keys do not exist", }, &cli.StringFlag{ Name: "source-folder", Usage: "source folder to use", Required: true, }, &cli.StringFlag{ Name: "dest-folder", Usage: "destination folder to use", Required: true, }, }, Action: initializeFS, } if err := app.Run(os.Args); err != nil { log.Fatal(err) } }