Skip to content

Commit

Permalink
2
Browse files Browse the repository at this point in the history
  • Loading branch information
DTLP committed Sep 22, 2023
1 parent c7cfe62 commit 8ba76b9
Show file tree
Hide file tree
Showing 5 changed files with 766 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.docs
docs/_index.md
docs/README.md
strongbox
bin
140 changes: 140 additions & 0 deletions cmd/strongbox/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package main

import (
"errors"
"flag"
"fmt"
"log"
"os"
"path/filepath"

"github.com/uw-labs/strongbox/strongbox"
)

var (
version = "dev"
commit = "none"
date = "unknown"
builtBy = "unknown"

keyLoader = strongbox.Key
kr strongbox.KeyRing
prefix = []byte("# STRONGBOX ENCRYPTED RESOURCE ;")
defaultPrefix = []byte("# STRONGBOX ENCRYPTED RESOURCE ; See https://github.com/uw-labs/strongbox\n")

errKeyNotFound = errors.New("key not found")

// flags
flagGitConfig = flag.Bool("git-config", false, "Configure git for strongbox use")
flagGenKey = flag.String("gen-key", "", "Generate a new key and add it to your strongbox keyring")
flagDecrypt = flag.Bool("decrypt", false, "Decrypt single resource")
flagKey = flag.String("key", "", "Private key to use to decrypt")
flagKeyRing = flag.String("keyring", "", "strongbox keyring file path, if not set default '$HOME/.strongbox_keyring' will be used")
flagRecursive = flag.Bool("recursive", false, "Recursively decrypt all files under given folder, must be used with -decrypt flag")

flagClean = flag.String("clean", "", "intended to be called internally by git")
flagSmudge = flag.String("smudge", "", "intended to be called internally by git")
flagDiff = flag.String("diff", "", "intended to be called internally by git")

flagVersion = flag.Bool("version", false, "Strongbox version")
)

func usage() {
fmt.Fprintf(os.Stderr, "Usage:\n\n")
fmt.Fprintf(os.Stderr, "\tstrongbox -git-config\n")
fmt.Fprintf(os.Stderr, "\tstrongbox [-keyring <keyring_file_path>] -gen-key key-name\n")
fmt.Fprintf(os.Stderr, "\tstrongbox [-keyring <keyring_file_path>] -decrypt -recursive <path>\n")
fmt.Fprintf(os.Stderr, "\tstrongbox -decrypt -recursive -key <key> <path>\n")
fmt.Fprintf(os.Stderr, "\tstrongbox -decrypt -key <key>\n")
fmt.Fprintf(os.Stderr, "\tstrongbox -version\n")
fmt.Fprintf(os.Stderr, "\nif -keyring flag is not set default file '$HOME/.strongbox_keyring' or '$STRONGBOX_HOME/.strongbox_keyring' will be used as keyring\n")
os.Exit(2)
}

func main() {
log.SetPrefix("strongbox: ")
log.SetFlags(log.LstdFlags | log.Lshortfile)

flag.Usage = usage
flag.Parse()

if *flagVersion || (flag.NArg() == 1 && flag.Arg(0) == "version") {
fmt.Printf("version=%s commit=%s date=%s builtBy=%s\n", version, commit, date, builtBy)
return
}

if *flagGitConfig {
strongbox.GitConfig()
return
}

if *flagDiff != "" {
strongbox.Diff(*flagDiff)
return
}

// Set up keyring file name
home := strongbox.DeriveHome()
kr = &strongbox.FileKeyRing{FileName: filepath.Join(home, ".strongbox_keyring")}

// if keyring flag is set replace default keyRing
if *flagKeyRing != "" {
kr = &strongbox.FileKeyRing{FileName: *flagKeyRing}
// verify keyring is valid
if err := kr.Load(); err != nil {
log.Fatalf("unable to load keyring file:%s err:%s", *flagKeyRing, err)
}
}

if *flagGenKey != "" {
strongbox.GenKey(*flagGenKey)
return
}

if *flagDecrypt {
// handle recursive
if *flagRecursive {
var err error

target := flag.Arg(0)
if target == "" {
target, err = os.Getwd()
if err != nil {
log.Fatalf("target path not provided and unable to get cwd err:%s", err)
}
}
// for recursive decryption 'key' flag is optional but if provided
// it should be valid and all encrypted file will be decrypted using it
dk, err := strongbox.Decode([]byte(*flagKey))
if err != nil && *flagKey != "" {
log.Fatalf("Unable to decode given private key %v", err)
}

if err = strongbox.RecursiveDecrypt(target, dk); err != nil {
log.Fatalln(err)
}
return
}

if *flagKey == "" {
log.Fatalf("Must provide a `-key` when using -decrypt")
}
strongbox.DecryptCLI(*flagKey)
return
}

if *flagRecursive {
log.Println("-recursive flag is only supported with -decrypt")
usage()
}

if *flagClean != "" {
strongbox.Clean(os.Stdin, os.Stdout, *flagClean)
return
}
if *flagSmudge != "" {
strongbox.Smudge(os.Stdin, os.Stdout, *flagSmudge)
return
}
}

85 changes: 85 additions & 0 deletions strongbox/keyring.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package strongbox

import (
"fmt"
"log"
"os"
"path/filepath"

"gopkg.in/yaml.v2"
)

type KeyRing interface {
Load() error
Save() error
AddKey(name string, keyID []byte, key []byte)
Key(keyID []byte) ([]byte, error)
}

type FileKeyRing struct {
FileName string
KeyEntries []keyEntry
}

type keyEntry struct {
Description string `yaml:"description"`
KeyID string `yaml:"key-id"`
Key string `yaml:"key"`
}

func (kr *FileKeyRing) AddKey(desc string, keyID []byte, key []byte) {
kr.KeyEntries = append(kr.KeyEntries, keyEntry{
Description: desc,
KeyID: string(Encode(keyID[:])),
Key: string(Encode(key[:])),
})
}

func (kr *FileKeyRing) Key(keyID []byte) ([]byte, error) {
b64 := string(Encode(keyID[:]))

for _, ke := range kr.KeyEntries {
if ke.KeyID == b64 {
dec, err := Decode([]byte(ke.Key))
if err != nil {
return []byte{}, err
}
if len(dec) != 32 {
return []byte{}, fmt.Errorf("unexpected length of key: %d", len(dec))
}
return dec, nil
}
}

return []byte{}, errKeyNotFound
}

func (kr *FileKeyRing) Load() error {

fmt.Println("kr.Load")
bytes, err := os.ReadFile(kr.FileName)
if err != nil {
return err
}

err = yaml.Unmarshal(bytes, kr)
return err
}

func (kr *FileKeyRing) Save() error {
ser, err := yaml.Marshal(kr)
if err != nil {
log.Fatal(err)
}

path := filepath.Dir(kr.FileName)
_, err = os.Stat(path)
if os.IsNotExist(err) {
err := os.MkdirAll(path, 0700)
if err != nil {
return fmt.Errorf("error creating strongbox home folder: %s", err)
}
}

return os.WriteFile(kr.FileName, ser, 0600)
}
Loading

0 comments on commit 8ba76b9

Please sign in to comment.