Fixed bug where encrypted files could not be downloaded after rerunning setup #64, output error message if incorrect key is provided during download instead of panic, added note about password entry on setup

This commit is contained in:
Marc Ole Bulling
2022-07-15 14:59:01 +02:00
parent 5bb338f91c
commit 23feaa241c
6 changed files with 85 additions and 56 deletions
+41 -28
View File
@@ -381,18 +381,43 @@ func getCloudConfig(formObjects *[]jsonFormObject) (*cloudconfig.CloudConfig, er
return &awsConfig, nil
}
func encryptionHasChanged(encLevel int, formObjects *[]jsonFormObject) (bool, error) {
if encLevel != configuration.Get().Encryption.Level {
return true, nil
}
if encLevel == encryption.LocalEncryptionInput || encLevel == encryption.FullEncryptionInput {
masterPw, err := getFormValueString(formObjects, "enc_pw")
if err != nil {
return true, err
}
return masterPw != "unc", nil
}
return false, nil
}
func parseEncryptionAndDelete(result *models.Configuration, formObjects *[]jsonFormObject) error {
encLevel, err := parseEncryptionLevel(formObjects)
if err != nil {
return err
}
generateNewEncConfig := true
if !isInitialSetup {
previousLevel := configuration.Get().Encryption.Level
if previousLevel != encLevel {
storage.DeleteAllEncrypted()
generateNewEncConfig, err = encryptionHasChanged(encLevel, formObjects)
if err != nil {
return err
}
}
if !generateNewEncConfig {
result.Encryption = configuration.Get().Encryption
return nil
}
if !isInitialSetup {
storage.DeleteAllEncrypted()
}
result.Encryption = models.Encryption{}
if encLevel == encryption.LocalEncryptionStored || encLevel == encryption.FullEncryptionStored {
cipher, err := encryption.GetRandomCipher()
@@ -409,11 +434,9 @@ func parseEncryptionAndDelete(result *models.Configuration, formObjects *[]jsonF
if encLevel == encryption.LocalEncryptionInput || encLevel == encryption.FullEncryptionInput {
result.Encryption.Salt = helper.GenerateRandomString(30)
result.Encryption.ChecksumSalt = helper.GenerateRandomString(30)
if !isPwLongEnough(masterPw) {
return errors.New("password is less than 6 characters long")
}
if !isInitialSetup && masterPw != "unc" {
storage.DeleteAllEncrypted()
const minLength = 8
if len(masterPw) < minLength {
return errors.New("password is less than " + strconv.Itoa(minLength) + " characters long")
}
result.Encryption.Checksum = encryption.PasswordChecksum(masterPw, result.Encryption.ChecksumSalt)
}
@@ -421,14 +444,6 @@ func parseEncryptionAndDelete(result *models.Configuration, formObjects *[]jsonF
return nil
}
func isPwLongEnough(pw string) bool {
const minLength = 6
if isInitialSetup || pw != "unc" {
return len(pw) >= minLength
}
return true
}
func parseEncryptionLevel(formObjects *[]jsonFormObject) (int, error) {
encLevelStr, err := getFormValueString(formObjects, "encrypt_sel")
if err != nil {
@@ -471,17 +486,16 @@ func splitAndTrim(input string) []string {
}
type setupView struct {
IsInitialSetup bool
LocalhostOnly bool
HasAwsFeature bool
IsDocker bool
Port int
OAuthUsers string
HeaderUsers string
Auth models.AuthenticationConfig
Settings models.Configuration
CloudSettings cloudconfig.CloudConfig
EncryptionLevel int
IsInitialSetup bool
LocalhostOnly bool
HasAwsFeature bool
IsDocker bool
Port int
OAuthUsers string
HeaderUsers string
Auth models.AuthenticationConfig
Settings models.Configuration
CloudSettings cloudconfig.CloudConfig
}
func (v *setupView) loadFromConfig() {
@@ -498,7 +512,6 @@ func (v *setupView) loadFromConfig() {
v.CloudSettings, _ = cloudconfig.Load()
v.OAuthUsers = strings.Join(settings.Authentication.OauthUsers, ";")
v.HeaderUsers = strings.Join(settings.Authentication.HeaderUsers, ";")
v.EncryptionLevel = settings.Encryption.Level
if strings.Contains(settings.Port, "localhost") || strings.Contains(settings.Port, "127.0.0.1") {
v.LocalhostOnly = true
+2 -15
View File
@@ -74,7 +74,7 @@ func TestEncryptionSetup(t *testing.T) {
test.IsEqualString(t, config.Encryption.Checksum, "")
input.EncryptionLevel.Value = "2"
input.EncryptionPassword.Value = "testpw"
input.EncryptionPassword.Value = "testpw12"
formObjects, err = input.toFormObject()
test.IsNil(t, err)
config, _, err = toConfiguration(&formObjects)
@@ -115,7 +115,7 @@ func TestEncryptionSetup(t *testing.T) {
_, ok = database.GetMetaDataById(id)
test.IsEqualBool(t, ok, true)
configuration.Get().Encryption.Level = 2
input.EncryptionPassword.Value = "otherpw"
input.EncryptionPassword.Value = "otherpw12"
id = testconfiguration.WriteEncryptedFile()
_, ok = database.GetMetaDataById(id)
test.IsEqualBool(t, ok, true)
@@ -519,19 +519,6 @@ func (s *setupValues) toFormObject() ([]jsonFormObject, error) {
return setupResult, err
}
func TestIsPwLongEnough(t *testing.T) {
isInitialSetup = true
test.IsEqualBool(t, isPwLongEnough("unc"), false)
test.IsEqualBool(t, isPwLongEnough("12345"), false)
test.IsEqualBool(t, isPwLongEnough("123456"), true)
test.IsEqualBool(t, isPwLongEnough("1234567"), true)
isInitialSetup = false
test.IsEqualBool(t, isPwLongEnough("unc"), true)
test.IsEqualBool(t, isPwLongEnough("12345"), false)
test.IsEqualBool(t, isPwLongEnough("123456"), true)
test.IsEqualBool(t, isPwLongEnough("1234567"), true)
}
func createInvalidSetupValues() []setupValues {
var result []setupValues
input := createInputInternalAuth()
@@ -546,8 +546,9 @@ function TestAWS(button) {
<div class="wizard-success">
<div class="alert alert-success">
<span class="create-server-name"></span>Gokapi has been set up <strong>successfully.</strong>
<span class="create-server-name"></span>Gokapi has been set up <strong>successfully</strong>.
</div>
If you configured Gokapi to use a password during startup, please enter it now in the console before clicking on continue.<br><br>
<a class="btn btn-success im-done">Continue</a>
</div>
@@ -720,7 +721,8 @@ function TestAWS(button) {
function validatePassword2(el) {
let value = el.val();
{{ if not .IsInitialSetup }}
if (value == "unc")
let previousEncLevel = {{ .Settings.Encryption.Level}};
if (value == "unc" && previousEncLevel == document.getElementById("encrypt_sel").value)
return true;
{{end}}
let minLenghtStatus = validateMinLength(el);
@@ -753,8 +755,8 @@ function TestAWS(button) {
{{ if .IsInitialSetup }}
encryptionSelectionChanged(0, true);
{{ else }}
document.getElementById("encrypt_sel").value={{.EncryptionLevel}};
encryptionSelectionChanged({{.EncryptionLevel}}, true);
document.getElementById("encrypt_sel").value={{.Settings.Encryption.Level}};
encryptionSelectionChanged({{.Settings.Encryption.Level}}, true);
{{ end }}
});
+22 -4
View File
@@ -14,6 +14,7 @@ import (
"golang.org/x/crypto/scrypt"
"io"
"log"
"os"
"time"
)
@@ -155,19 +156,36 @@ func Encrypt(encInfo *models.EncryptionInfo, input io.Reader, output io.Writer)
return err
}
// DecryptReader modifies a reader so it can decrypt encrypted files
func DecryptReader(encInfo models.EncryptionInfo, input io.Reader, output io.Writer) error {
func createDecryptReader(encInfo models.EncryptionInfo, input io.Reader) (*sio.DecReader, error) {
key, err := GetCipherFromFile(encInfo)
if err != nil {
return err
return nil, err
}
stream := getStream(key)
nonce := make([]byte, stream.NonceSize()) // Nonce is not used
reader := stream.DecryptReader(input, nonce, nil)
return stream.DecryptReader(input, nonce, nil), nil
}
// DecryptReader modifies a reader so it can decrypt encrypted files
func DecryptReader(encInfo models.EncryptionInfo, input io.Reader, output io.Writer) error {
reader, err := createDecryptReader(encInfo, input)
if err != nil {
return err
}
_, err = io.Copy(output, reader)
return err
}
// IsCorrectKey checks if correct key is being used. This does not check for complete file authentication.
func IsCorrectKey(encInfo models.EncryptionInfo, input *os.File) bool {
_, err := createDecryptReader(encInfo, input)
if err != nil {
fmt.Println(err)
return false
}
return true
}
// GetDecryptWriter returns a writer that can decrypt encrypted files
func GetDecryptWriter(cipherKey []byte, input io.Writer) (io.Writer, error) {
stream := getStream(cipherKey)
+8 -1
View File
@@ -341,13 +341,20 @@ func ServeFile(file models.File, w http.ResponseWriter, r *http.Request, forceDo
return
}
fileData, size := getFileHandler(file, configuration.Get().DataDir)
if file.Encryption.IsEncrypted && !RequiresClientDecryption(file) {
if !encryption.IsCorrectKey(file.Encryption, fileData) {
w.Write([]byte("Internal error - Error decrypting file, source data might be damaged or an incorrect key has been used"))
return
}
}
statusId := downloadstatus.SetDownload(file)
writeDownloadHeaders(file, w, forceDownload)
if file.Encryption.IsEncrypted && !RequiresClientDecryption(file) {
err := encryption.DecryptReader(file.Encryption, fileData, w)
if err != nil {
w.Write([]byte("Error decrypting file"))
panic(err)
fmt.Println(err)
return
}
} else {
w.Header().Set("Content-Length", strconv.FormatInt(size, 10))
+6 -4
View File
@@ -440,7 +440,7 @@ func TestDuplicateFile(t *testing.T) {
func TestServeFile(t *testing.T) {
file, result := GetFile(idNewFile)
test.IsEqualBool(t, result, true)
r := httptest.NewRequest("GET", "/upload", nil)
r := httptest.NewRequest("GET", "/", nil)
w := httptest.NewRecorder()
ServeFile(file, w, r, true)
_, result = GetFile(idNewFile)
@@ -459,7 +459,7 @@ func TestServeFile(t *testing.T) {
test.IsEqualBool(t, ok, true)
ok = aws.Init(config.Aws)
test.IsEqualBool(t, ok, true)
r = httptest.NewRequest("GET", "/upload", nil)
r = httptest.NewRequest("GET", "/", nil)
w = httptest.NewRecorder()
file, result = GetFile("awsTest1234567890123")
test.IsEqualBool(t, result, true)
@@ -475,7 +475,7 @@ func TestServeFile(t *testing.T) {
test.IsNil(t, err)
file = newFile.File
database.SaveMetaData(file)
r = httptest.NewRequest("GET", "/upload", nil)
r = httptest.NewRequest("GET", "/", nil)
w = httptest.NewRecorder()
cipher, err := encryption.GetRandomCipher()
test.IsNil(t, err)
@@ -488,8 +488,10 @@ func TestServeFile(t *testing.T) {
file.Encryption.IsEncrypted = true
file.Encryption.DecryptionKey = cipher
file.Encryption.Nonce = nonce
defer test.ExpectPanic(t)
r = httptest.NewRequest("GET", "/", nil)
w = httptest.NewRecorder()
ServeFile(file, w, r, true)
test.ResponseBodyContains(t, w, "Error decrypting file")
}
func TestCleanUp(t *testing.T) {