diff --git a/cmd/server/main.go b/cmd/server/main.go index 6728948..6d15d0c 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -4,24 +4,12 @@ import ( "log" "os" - "github.com/AYM1607/ccclip/internal/config" "github.com/AYM1607/ccclip/internal/server" ) func main() { - privateKeyPath := os.Getenv("CCCLIP_PRIVATE_KEY") - publicKeyPath := os.Getenv("CCCLIP_PUBLIC_KEY") - databaseLocation := os.Getenv("CCCLIP_DATABASE_LOCATION") port := os.Getenv("CCCLIP_PORT") - if publicKeyPath == "" || privateKeyPath == "" { - log.Fatalf("database location and public and privae keys must be provided") - } - - config.Default.PrivateKeyPath = privateKeyPath - config.Default.PublicKeyPath = publicKeyPath - config.Default.DatabaseLocation = databaseLocation - if port == "" { port = "8080" } diff --git a/fly.toml b/fly.toml index 3e80aa4..8f81626 100644 --- a/fly.toml +++ b/fly.toml @@ -10,7 +10,6 @@ primary_region = "sea" dockerfile = "./cmd/server/Dockerfile" [env] - CCCLIP_LOAD_RAW_KEYS = "true" CCCLIP_PORT = "3000" CCCLIP_DATABASE_LOCATION = "/litefs/ccclip.db" diff --git a/internal/configfile/config.go b/internal/configfile/config.go index 72d4e11..6069612 100644 --- a/internal/configfile/config.go +++ b/internal/configfile/config.go @@ -55,20 +55,20 @@ func Write(c ConfigFile) error { func LoadPrivateKey() (*ecdh.PrivateKey, error) { fp := path.Join(Path, PrivateKeyFileName) - return crypto.LoadPrivateKey(fp) + return crypto.LoadPrivateKeyFromFile(fp) } func LoadPublicKey() (*ecdh.PublicKey, error) { fp := path.Join(Path, PublicKeyFileName) - return crypto.LoadPublicKey(fp) + return crypto.LoadPublicKeyFromFile(fp) } func SavePrivateKey(k *ecdh.PrivateKey) error { fp := path.Join(Path, PrivateKeyFileName) - return crypto.SavePrivateKey(fp, k) + return crypto.SavePrivateKeyToFile(fp, k) } func SavePublicKey(k *ecdh.PublicKey) error { fp := path.Join(Path, PublicKeyFileName) - return crypto.SavePublicKey(fp, k) + return crypto.SavePublicKeyToFile(fp, k) } diff --git a/internal/server/keys.go b/internal/server/keys.go new file mode 100644 index 0000000..cca340a --- /dev/null +++ b/internal/server/keys.go @@ -0,0 +1,40 @@ +package server + +import ( + "crypto/ecdh" + "errors" + "os" + + "github.com/AYM1607/ccclip/pkg/crypto" +) + +const ( + privateKeyEnv = "CCCLIP_PRIVATE_KEY" + publicKeyEnv = "CCCLIP_PUBLIC_KEY" + privateKeyPathEnv = "CCCLIP_PRIVATE_KEY_PATH" + publicKeyPathEnv = "CCCLIP_PUBLIC_KEY_PATH" +) + +func loadKeys() (*ecdh.PrivateKey, *ecdh.PublicKey, error) { + // Prioritize explicit keys over files. + var pvk *ecdh.PrivateKey + var pbk *ecdh.PublicKey + + if b64PrivateKey := os.Getenv(privateKeyEnv); b64PrivateKey != "" { + pvk = crypto.PrivateKeyFromB64([]byte(b64PrivateKey)) + } else if privateKeyPath := os.Getenv(privateKeyPathEnv); privateKeyPath != "" { + pvk = crypto.LoadPrivateKeyFromFile(privateKeyPath) + } else { + return nil, nil, errors.New("no private key was found") + } + + if b64PublicKey := os.Getenv(publicKeyEnv); b64PublicKey != "" { + pbk = crypto.PublicKeyFromB64([]byte(b64PublicKey)) + } else if publicKeyPath := os.Getenv(publicKeyPathEnv); publicKeyPath != "" { + pbk = crypto.LoadPublicKeyFromFile(publicKeyPath) + } else { + return nil, nil, errors.New("to public key was found") + } + + return pvk, pbk, nil +} diff --git a/internal/server/server.go b/internal/server/server.go index 79ea2a2..aaa74d9 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -3,16 +3,16 @@ package server import ( "crypto/ecdh" "encoding/json" + "fmt" "log" "net/http" + "os" "github.com/gorilla/mux" "golang.org/x/crypto/bcrypt" - "github.com/AYM1607/ccclip/internal/config" "github.com/AYM1607/ccclip/internal/db" "github.com/AYM1607/ccclip/pkg/api" - "github.com/AYM1607/ccclip/pkg/crypto" ) func New(addr string) *http.Server { @@ -24,7 +24,10 @@ func New(addr string) *http.Server { } } -const minPasswordWork = 12 +const ( + minPasswordWork = 12 + dbLocationEnv = "CCCLIP_DATABASE_LOCATION" +) type controller struct { store db.DB @@ -37,20 +40,16 @@ type controller struct { func newHttpHandler() http.Handler { r := mux.NewRouter() - pbk, err := crypto.LoadPublicKey(config.Default.PublicKeyPath) + pvk, pbk, err := loadKeys() if err != nil { - panic("could not load server's public key") - } - pvk, err := crypto.LoadPrivateKey(config.Default.PrivateKeyPath) - if err != nil { - panic("could not load server's private key") + panic(fmt.Errorf("could not load keys for the server: %w", err)) } var store db.DB - if config.Default.DatabaseLocation == "" { - store = db.NewLocalDB() + if dbLocation := os.Getenv(dbLocationEnv); dbLocation != "" { + store = db.NewSQLiteDB(dbLocation) } else { - store = db.NewSQLiteDB(config.Default.DatabaseLocation) + store = db.NewLocalDB() } c := &controller{ diff --git a/pkg/crypto/key_files.go b/pkg/crypto/key_files.go new file mode 100644 index 0000000..87b5c0e --- /dev/null +++ b/pkg/crypto/key_files.go @@ -0,0 +1,50 @@ +package crypto + +import ( + "crypto/ecdh" + "encoding/base64" + "os" +) + +func LoadPrivateKeyFromFile(fp string) *ecdh.PrivateKey { + kb, err := loadKeyFile(fp) + if err != nil { + panic(err) + } + return PrivateKeyFromBytes(kb) +} + +func LoadPublicKeyFromFile(fp string) *ecdh.PublicKey { + kb, err := loadKeyFile(fp) + if err != nil { + panic(err) + } + + return PublicKeyFromBytes(kb) +} + +func SavePrivateKeyToFile(fp string, k *ecdh.PrivateKey) error { + return saveKeyFile(fp, k.Bytes(), privateKeyFileMode) +} + +func SavePublicKeyToFile(fp string, k *ecdh.PublicKey) error { + return saveKeyFile(fp, k.Bytes(), publicKeyFileMode) +} + +func loadKeyFile(fp string) ([]byte, error) { + b64Key, err := os.ReadFile(fp) + if err != nil { + return nil, err + } + + keyBytes := make([]byte, KeySize) + base64.StdEncoding.Decode(keyBytes, b64Key) + return keyBytes, nil +} + +func saveKeyFile(fp string, key []byte, fm os.FileMode) error { + b64Key := make([]byte, base64.StdEncoding.EncodedLen(len(key))) + base64.StdEncoding.Encode(b64Key, key) + + return os.WriteFile(fp, b64Key, fm) +} diff --git a/pkg/crypto/keys.go b/pkg/crypto/keys.go index ae84d08..db983ec 100644 --- a/pkg/crypto/keys.go +++ b/pkg/crypto/keys.go @@ -64,6 +64,24 @@ func NewSharedKey(local *ecdh.PrivateKey, remote *ecdh.PublicKey, direction Dire return key } +func PrivateKeyFromB64(encodedKey []byte) *ecdh.PrivateKey { + keyBytes := make([]byte, base64.StdEncoding.DecodedLen(len(encodedKey))) + _, err := base64.StdEncoding.Decode(keyBytes, encodedKey) + if err != nil { + panic(fmt.Errorf("could not decode base64 private key: %w", err)) + } + return PrivateKeyFromBytes(keyBytes) +} + +func PublicKeyFromB64(encodedKey []byte) *ecdh.PublicKey { + keyBytes := make([]byte, base64.StdEncoding.DecodedLen(len(encodedKey))) + _, err := base64.StdEncoding.Decode(keyBytes, encodedKey) + if err != nil { + panic(fmt.Errorf("could not decode base64 public key: %w", err)) + } + return PublicKeyFromBytes(keyBytes) +} + func PrivateKeyFromBytes(keyBytes []byte) *ecdh.PrivateKey { key, err := ecdh.X25519().NewPrivateKey(keyBytes) if err != nil { @@ -79,61 +97,3 @@ func PublicKeyFromBytes(keyBytes []byte) *ecdh.PublicKey { } return key } - -func LoadPrivateKey(fp string) (*ecdh.PrivateKey, error) { - var kb []byte - var err error - if os.Getenv("CCCLIP_LOAD_RAW_KEYS") == "" { - kb, err = loadKey(fp) - } else { - kb = make([]byte, KeySize) - base64.StdEncoding.Decode(kb, []byte(fp)) - } - if err != nil { - return nil, err - } - - return PrivateKeyFromBytes(kb), nil -} - -func LoadPublicKey(fp string) (*ecdh.PublicKey, error) { - var kb []byte - var err error - if os.Getenv("CCCLIP_LOAD_RAW_KEYS") == "" { - kb, err = loadKey(fp) - } else { - kb = make([]byte, KeySize) - base64.StdEncoding.Decode(kb, []byte(fp)) - } - if err != nil { - return nil, err - } - - return PublicKeyFromBytes(kb), nil -} - -func SavePrivateKey(fp string, k *ecdh.PrivateKey) error { - return saveKey(fp, k.Bytes(), privateKeyFileMode) -} - -func SavePublicKey(fp string, k *ecdh.PublicKey) error { - return saveKey(fp, k.Bytes(), publicKeyFileMode) -} - -func loadKey(fp string) ([]byte, error) { - b64Key, err := os.ReadFile(fp) - if err != nil { - return nil, err - } - - keyBytes := make([]byte, KeySize) - base64.StdEncoding.Decode(keyBytes, b64Key) - return keyBytes, nil -} - -func saveKey(fp string, key []byte, fm os.FileMode) error { - b64Key := make([]byte, base64.StdEncoding.EncodedLen(len(key))) - base64.StdEncoding.Encode(b64Key, key) - - return os.WriteFile(fp, b64Key, fm) -}