diff --git a/README.md b/README.md index affdf68..a49a0f7 100644 --- a/README.md +++ b/README.md @@ -119,8 +119,6 @@ multisql encrypt pgpass.sec pgpass Program zapyta o nowe hasło a następnie zaszyfruje treść pliku pgpass i zapisze go do pliku pgpass.sec. -> Uwaga: plik pgpass.sec zostanie napisany bez pytania. - W konfiguracji należy ustawić plik wartość `"Passfile"` na `pgpass.sec`. Zaszyfrowany plik można odszyfrować i zapisać do pliku jawnego lub podejrzeć: @@ -154,4 +152,9 @@ Windows, powershell: $env:MULTISQLPASS = "abc" ``` -Użycie opcji `-P` powoduje, że zmienna środowiskowa jest ignorowana. +Użycie opcji `-P` powoduje, że zmienna środowiskowa jest ignorowana i hasło pobierane jest z klawiatury. + +> Uwaga: Od wersji 0.2.3 wykorzystywany jest format szyfrowania +> [age-encryption](https://age-encryption.org/). +> Plik haseł można więc szyfrować i deszyftować również narzędziem +> `age` (https://github.com/FiloSottile/age/releases/tag/v1.0.0) diff --git a/cfg/params.go b/cfg/params.go index ef504d2..13ff326 100644 --- a/cfg/params.go +++ b/cfg/params.go @@ -129,7 +129,8 @@ plik zaszyfrować: multsql encrypt pgpass.encrypted pgpass -Hasło jest pobierane ze zmiennej środowiskowej %s lub z klawiatury, jeśli użyto opcji -P. +Przy użyciu (odszyfrowaniu) pliku, hasło jest pobierane ze zmiennej +środowiskowej %s lub z klawiatury, jeśli użyto opcji -P. `, MULTISQLPASS) diff --git a/cmd/multisql/multisql.go b/cmd/multisql/multisql.go index f862f3d..cbc3aa5 100644 --- a/cmd/multisql/multisql.go +++ b/cmd/multisql/multisql.go @@ -87,6 +87,7 @@ func encryption(args []string, params cfg.Parameters) { err = pass.EncryptFile(password, args[1], plain) if err != nil { + _ = os.Remove(args[1]) // jeśli błąd to usuń plik log.Fatalf("Błąd szyfrowania pliku: %v", err) } case "decrypt": diff --git a/go.mod b/go.mod index 112e615..28fd739 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,11 @@ module baal.ar76.eu/x/pub/multisql go 1.19 require ( - golang.org/x/crypto v0.3.0 + filippo.io/age v1.0.0 golang.org/x/term v0.2.0 ) -require golang.org/x/sys v0.2.0 // indirect +require ( + golang.org/x/crypto v0.3.0 // indirect + golang.org/x/sys v0.2.0 // indirect +) diff --git a/go.sum b/go.sum index c907071..b0cfe2c 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +filippo.io/age v1.0.0 h1:V6q14n0mqYU3qKFkZ6oOaF9oXneOviS3ubXsSVBRSzc= +filippo.io/age v1.0.0/go.mod h1:PaX+Si/Sd5G8LgfCwldsSba3H1DDQZhIhFGkhbHaBq8= golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= diff --git a/pass/crypt.go b/pass/crypt.go index 9c39588..539e50b 100644 --- a/pass/crypt.go +++ b/pass/crypt.go @@ -1,109 +1,65 @@ package pass import ( - "crypto/aes" - "crypto/cipher" - "crypto/rand" + "bytes" "fmt" + "io" "os" - "golang.org/x/crypto/scrypt" + "filippo.io/age" + "filippo.io/age/armor" ) -func DeriveKey(password, salt []byte) ([]byte, []byte, error) { - if salt == nil { - salt = make([]byte, 32) - if _, err := rand.Read(salt); err != nil { - return nil, nil, err - } - } - - key, err := scrypt.Key(password, salt, 32768, 8, 1, 32) - if err != nil { - return nil, nil, fmt.Errorf("błąd generowania klucza: %w", err) - } - - return key, salt, nil -} - -func Encrypt(key, data []byte) ([]byte, error) { - blockCipher, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - - gcm, err := cipher.NewGCM(blockCipher) - if err != nil { - return nil, err - } - - nonce := make([]byte, gcm.NonceSize()) - if _, err = rand.Read(nonce); err != nil { - return nil, err - } - - ciphertext := gcm.Seal(nonce, nonce, data, nil) - - return ciphertext, nil -} - -func Decrypt(key, data []byte) ([]byte, error) { - blockCipher, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - - gcm, err := cipher.NewGCM(blockCipher) - if err != nil { - return nil, err - } - - nonce, ciphertext := data[:gcm.NonceSize()], data[gcm.NonceSize():] - - plaintext, err := gcm.Open(nil, nonce, ciphertext, nil) - if err != nil { - return nil, err - } - - return plaintext, nil -} - func DecryptFile(password []byte, file string) ([]byte, error) { - data, err := os.ReadFile(file) + identity, err := age.NewScryptIdentity(string(password)) if err != nil { - return nil, err + return nil, fmt.Errorf("błąd przetwarzania hasła: %w", err) } - salt := data[:32] // - data = data[32:] - key, _, err := DeriveKey(password, salt) + fd, err := os.Open(file) if err != nil { - return nil, err + return nil, fmt.Errorf("błąd otwarcia pliku: %w", err) } - return Decrypt(key, data) + defer fd.Close() + + in := armor.NewReader(fd) + + r, err := age.Decrypt(in, identity) + if err != nil { + return nil, fmt.Errorf("błąd deszyfrowania #01: %w", err) + } + + out := &bytes.Buffer{} + if _, err := io.Copy(out, r); err != nil { + return nil, fmt.Errorf("błąd deszyfrowania #02: %v", err) + } + return out.Bytes(), nil } func EncryptFile(password []byte, file string, data []byte) error { - key, salt, err := DeriveKey(password, nil) + recipient, err := age.NewScryptRecipient(string(password)) if err != nil { - return err + return fmt.Errorf("błąd przetwarzania hasła: %w", err) } - encrypted, err := Encrypt(key, data) + fd, err := os.OpenFile(file, os.O_WRONLY | os.O_CREATE | os.O_EXCL, 0o600) if err != nil { - return err + return fmt.Errorf("błąd tworzenia pliku: %w", err) } - fd, err := os.Create(file) + defer fd.Close() + + out := armor.NewWriter(fd) // armor + defer out.Close() + + w, err := age.Encrypt(out, recipient) if err != nil { - return err + return fmt.Errorf("błąd szyfrowania #01: %w", err) } - _, err = fd.Write(salt) + _, err = w.Write(data) if err != nil { - return err + return fmt.Errorf("błąd szyfrowania #02: %w", err) } - _, err = fd.Write(encrypted) + err = w.Close() if err != nil { - return err + return fmt.Errorf("błąd szyfrowania #03: %w", err) } - return fd.Close() + return nil } - -