From a317342aa68fd3dbbda7446368570efc5100b6b4 Mon Sep 17 00:00:00 2001 From: imterah Date: Fri, 22 Nov 2024 07:25:17 -0500 Subject: [PATCH] feature: Implements basic key generation. --- bofs/fs.go | 33 +++ go.mod | 10 + go.sum | 15 ++ main.go | 188 +++++++++++++++++- tmp/key | 51 +++++ ...39a3ee5e6b4b0d3255bfef95601890afd80709.pub | 13 ++ 6 files changed, 308 insertions(+), 2 deletions(-) create mode 100644 bofs/fs.go create mode 100644 go.sum create mode 100644 tmp/key create mode 100644 tmp/src/keys/da39a3ee5e6b4b0d3255bfef95601890afd80709.pub diff --git a/bofs/fs.go b/bofs/fs.go new file mode 100644 index 0000000..35796a5 --- /dev/null +++ b/bofs/fs.go @@ -0,0 +1,33 @@ +package bofs + +import ( + "context" + "syscall" + + "github.com/hanwen/go-fuse/v2/fs" + "github.com/hanwen/go-fuse/v2/fuse" +) + +type BoronInode struct { + fs.Inode +} + +func (r *BoronInode) OnAdd(ctx context.Context) { + ch := r.NewPersistentInode( + ctx, &fs.MemRegularFile{ + Data: []byte("file.txt"), + Attr: fuse.Attr{ + Mode: 0644, + }, + }, fs.StableAttr{Ino: 2}) + + r.AddChild("file.txt", ch, false) +} + +func (r *BoronInode) Getattr(ctx context.Context, fh fs.FileHandle, out *fuse.AttrOut) syscall.Errno { + out.Mode = 0755 + return 0 +} + +var _ = (fs.NodeGetattrer)((*BoronInode)(nil)) +var _ = (fs.NodeOnAdder)((*BoronInode)(nil)) diff --git a/go.mod b/go.mod index 0395aab..b8495ff 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,13 @@ module git.greysoh.dev/imterah/boron go 1.23.2 + +require ( + github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect + github.com/hanwen/go-fuse v1.0.0 // indirect + github.com/hanwen/go-fuse/v2 v2.7.0 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/urfave/cli/v2 v2.27.5 // indirect + github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect + golang.org/x/sys v0.27.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..bb4fd34 --- /dev/null +++ b/go.sum @@ -0,0 +1,15 @@ +github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= +github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/hanwen/go-fuse v1.0.0 h1:GxS9Zrn6c35/BnfiVsZVWmsG803xwE7eVRDvcf/BEVc= +github.com/hanwen/go-fuse v1.0.0/go.mod h1:unqXarDXqzAk0rt98O2tVndEPIpUgLD9+rwFisZH3Ok= +github.com/hanwen/go-fuse/v2 v2.7.0 h1:b3khst81C011GCwv9BF7PUa4lbbzIdRFPgAxRlgGUvw= +github.com/hanwen/go-fuse/v2 v2.7.0/go.mod h1:ugNaD/iv5JYyS1Rcvi57Wz7/vrLQJo10mmketmoef48= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= +github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/main.go b/main.go index f7b60bd..b3136d6 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,191 @@ package main -import "fmt" +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() { - fmt.Println("Hello, world!") + 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) + } } diff --git a/tmp/key b/tmp/key new file mode 100644 index 0000000..0fae318 --- /dev/null +++ b/tmp/key @@ -0,0 +1,51 @@ +-----BEGIN PRIVATE KEY----- +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 +-----END PRIVATE KEY----- diff --git a/tmp/src/keys/da39a3ee5e6b4b0d3255bfef95601890afd80709.pub b/tmp/src/keys/da39a3ee5e6b4b0d3255bfef95601890afd80709.pub new file mode 100644 index 0000000..95de1a3 --- /dev/null +++ b/tmp/src/keys/da39a3ee5e6b4b0d3255bfef95601890afd80709.pub @@ -0,0 +1,13 @@ +-----BEGIN PUBLIC KEY----- +MIICCgKCAgEAp7kY9Pa64fgcwaI38bboLarFjsS2P6V93Kwt98bjC1avPrclaIUM +phYYVT4hRnhhkP2LNwS+Qk/rWr8QuRdaSPSjlospgMQ20VfDIZBRYYU/sTC4hhuC +IMStnukCyVTrFgHsWMT7A+dd7OWxiZOQbO7FeFdds2vs/5T5ZOGoXAXxTHA0+Bft +PtvUjm/Edz7csam73ItGlaF8zFZqSUQa6Zgl1+LYvTrEyOymTGlipo7p86cGveLF +/i9v3NfPB7QbjZHPaS4DIz+zzaSP3glHXudI8aZJB1lKKQOGl3JHRiY0PhIPwZRB +KyXkYfomAaK++Wvr78a1mYMYRvPoEZbUI7fNAQrewVSsYSr5PgnhTrcWR/6URUB9 +/ys3OJHHQnhLYRrJfpSroUZuVKYFUSSVwJe74thJJUbbCgzhMDpHuRM6YLNwXtOy +53iyYY/N61bUlESg/VoVRRmMmR4srE2jfTkOhaX7nzLWxIJ+wc9SLMItEJwqn/57 +OPdBU7AefEy9CER35XJjy6wku3VhdHISYKunCW3uS/kA+xlTEgrdAyBKWi870q5G +Sj6uCBmJyNCMwj+bCC+sWEbOhVKHBjXdOervyP4/CWF9XrCrqndzgcupXMdCGQG9 +H+BmXN1bOgIosd38hjdjLo+yiKo4MmhLWjBCVs9WFW5Tm8m7LZ50/lUCAwEAAQ== +-----END PUBLIC KEY-----