mirror of
https://github.com/Forceu/Gokapi.git
synced 2026-05-19 22:30:22 -05:00
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:
@@ -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
|
||||
|
||||
@@ -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 }}
|
||||
});
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user