diff --git a/app/mobile/src/constants/Strings.js b/app/mobile/src/constants/Strings.js index 3a8b25aa..33e953eb 100644 --- a/app/mobile/src/constants/Strings.js +++ b/app/mobile/src/constants/Strings.js @@ -206,7 +206,7 @@ const Strings = [ selectTopic: 'Select Topic for Sharing', mfaTitle: 'Multi-Factor Authentication', - mfaSteps: 'Store the SHA256 secret and confirm the verification code', + mfaSteps: 'Store the secret and confirm the verification code', mfaError: 'verification code error', mfaDisabled: 'verification temporarily disabled', mfaConfirm: 'Confirm', @@ -417,7 +417,7 @@ const Strings = [ selectTopic: 'Choisissez le sujet à partager', mfaTitle: 'Authentification Multi-Factor', - mfaSteps: 'Enregistrez le secret SHA256 et confirmez le code de vérification', + mfaSteps: 'Enregistrez le secret et confirmez le code de vérification', mfaEnter: 'Entrez votre code de vérification', mfaError: 'erreur de code de vérification', mfaDisabled: 'vérification temporairement désactivée', @@ -629,7 +629,7 @@ const Strings = [ selectTopic: 'Elija un tema para compartir', mfaTitle: 'Autenticación de Dos Factores', - mfaSteps: 'Guarde el secreto SHA256 y confirme el código de verificación', + mfaSteps: 'Guarde el secreto y confirme el código de verificación', mfaEnter: 'Ingresa tu código de verificación', mfaError: 'error de código de verificación', mfaDisabled: 'verificación temporalmente deshabilitada', @@ -841,7 +841,7 @@ const Strings = [ selectTopic: 'Wählen Sie ein Thema zum Teilen aus', mfaTitle: 'Zwei-Faktor-Authentifizierung', - mfaSteps: 'Speichern Sie das SHA256-Geheimnis und bestätigen Sie den Bestätigungscode', + mfaSteps: 'Speichern Sie das Geheimnis und bestätigen Sie den Bestätigungscode', mfaEnter: 'Geben Sie Ihren Bestätigungs-Code ein', mfaError: 'Verifizierungscodefehler', mfaDisabled: 'Verifizierung vorübergehend deaktiviert', @@ -1038,7 +1038,7 @@ const Strings = [ selectTopic: 'Escolha o tópico para compartilhar', mfaTitle: 'Autenticação de Dois Fatores', - mfaSteps: 'Salve o segredo SHA256 e confirme o código de verificação', + mfaSteps: 'Salve o segredo e confirme o código de verificação', mfaEnter: 'Digite seu código de verificação', mfaError: 'erro de código de verificação', mfaDisabled: 'verificação temporariamente desativada', @@ -1233,7 +1233,7 @@ const Strings = [ selectTopic: 'Выберите тему для обмена', mfaTitle: 'Двухфакторная аутентификация', - mfaSteps: 'Сохраните секрет SHA256 и подтвердите код подтверждения', + mfaSteps: 'Сохраните секрет и подтвердите код подтверждения', mfaEnter: 'Введите Ваш верификационный код', mfaError: 'ошибка проверочного кода', mfaDisabled: 'проверка временно отключена', diff --git a/net/server/internal/api_addAccountApp.go b/net/server/internal/api_addAccountApp.go index e2b02093..43c5fbc3 100644 --- a/net/server/internal/api_addAccountApp.go +++ b/net/server/internal/api_addAccountApp.go @@ -34,7 +34,11 @@ func AddAccountApp(w http.ResponseWriter, r *http.Request) { return; } - opts := totp.ValidateOpts{Period: 30, Skew: 1, Digits: otp.DigitsSix, Algorithm: otp.AlgorithmSHA256} + algorithm := otp.AlgorithmSHA256; + if account.MFAAlgorithm == APPMFASHA1 { + algorithm = otp.AlgorithmSHA1 + } + opts := totp.ValidateOpts{Period: 30, Skew: 1, Digits: otp.DigitsSix, Algorithm: algorithm} if valid, _ := totp.ValidateCustom(code, account.MFASecret, time.Now(), opts); !valid { err := store.DB.Transaction(func(tx *gorm.DB) error { if account.MFAFailedTime + APPMFAFailPeriod > curTime { diff --git a/net/server/internal/api_addAdminMFAuth.go b/net/server/internal/api_addAdminMFAuth.go index c7bd97c6..aef5d98c 100644 --- a/net/server/internal/api_addAdminMFAuth.go +++ b/net/server/internal/api_addAdminMFAuth.go @@ -25,7 +25,7 @@ func AddAdminMFAuth(w http.ResponseWriter, r *http.Request) { Issuer: APPMFAIssuer, AccountName: "admin", Digits: otp.DigitsSix, - Algorithm: otp.AlgorithmSHA256, + Algorithm: otp.AlgorithmSHA1, }) err = store.DB.Transaction(func(tx *gorm.DB) error { diff --git a/net/server/internal/api_addMultiFactorAuth.go b/net/server/internal/api_addMultiFactorAuth.go index 323ccb9e..ba135c3f 100644 --- a/net/server/internal/api_addMultiFactorAuth.go +++ b/net/server/internal/api_addMultiFactorAuth.go @@ -24,7 +24,7 @@ func AddMultiFactorAuth(w http.ResponseWriter, r *http.Request) { Issuer: APPMFAIssuer, AccountName: account.Handle, Digits: otp.DigitsSix, - Algorithm: otp.AlgorithmSHA256, + Algorithm: otp.AlgorithmSHA1, }) err = store.DB.Transaction(func(tx *gorm.DB) error { diff --git a/net/server/internal/api_setAdminAccess.go b/net/server/internal/api_setAdminAccess.go index 30426a4f..f25db66a 100644 --- a/net/server/internal/api_setAdminAccess.go +++ b/net/server/internal/api_setAdminAccess.go @@ -40,8 +40,13 @@ func SetAdminAccess(w http.ResponseWriter, r *http.Request) { return; } - secret := getStrConfigValue(CNFMFASecret, ""); - opts := totp.ValidateOpts{Period: 30, Skew: 1, Digits: otp.DigitsSix, Algorithm: otp.AlgorithmSHA256} + secret := getStrConfigValue(CNFMFASecret, "") + algorithm := getStrConfigValue(CNFMFAAlgorithm, APPMFASHA256) + mfaAlgorithm := otp.AlgorithmSHA256 + if algorithm == APPMFASHA1 { + mfaAlgorithm = otp.AlgorithmSHA1 + } + opts := totp.ValidateOpts{Period: 30, Skew: 1, Digits: otp.DigitsSix, Algorithm: mfaAlgorithm} if valid, _ := totp.ValidateCustom(code, secret, time.Now(), opts); !valid { err := store.DB.Transaction(func(tx *gorm.DB) error { if failedTime + APPMFAFailPeriod > curTime { diff --git a/net/server/internal/api_setAdminMFAuth.go b/net/server/internal/api_setAdminMFAuth.go index 66ebd516..016758e2 100644 --- a/net/server/internal/api_setAdminMFAuth.go +++ b/net/server/internal/api_setAdminMFAuth.go @@ -38,39 +38,44 @@ func SetAdminMFAuth(w http.ResponseWriter, r *http.Request) { return; } + mfaAlgorithm := APPMFASHA1 secret := getStrConfigValue(CNFMFASecret, ""); - opts := totp.ValidateOpts{Period: 30, Skew: 1, Digits: otp.DigitsSix, Algorithm: otp.AlgorithmSHA256} + opts := totp.ValidateOpts{Period: 30, Skew: 1, Digits: otp.DigitsSix, Algorithm: otp.AlgorithmSHA1} if valid, _ := totp.ValidateCustom(code, secret, time.Now(), opts); !valid { - err := store.DB.Transaction(func(tx *gorm.DB) error { - if failedTime + APPMFAFailPeriod > curTime { - if res := tx.Clauses(clause.OnConflict{ - Columns: []clause.Column{{Name: "config_id"}}, - DoUpdates: clause.AssignmentColumns([]string{"num_value"}), - }).Create(&store.Config{ConfigID: CNFMFAFailedCount, NumValue: failedCount + 1}).Error; res != nil { - return res - } - } else { - if res := tx.Clauses(clause.OnConflict{ - Columns: []clause.Column{{Name: "config_id"}}, - DoUpdates: clause.AssignmentColumns([]string{"num_value"}), - }).Create(&store.Config{ConfigID: CNFMFAFailedTime, NumValue: curTime}).Error; res != nil { - return res - } - if res := tx.Clauses(clause.OnConflict{ - Columns: []clause.Column{{Name: "config_id"}}, - DoUpdates: clause.AssignmentColumns([]string{"num_value"}), - }).Create(&store.Config{ConfigID: CNFMFAFailedCount, NumValue: 1}).Error; res != nil { - return res + mfaAlgorithm = APPMFASHA256 + opts := totp.ValidateOpts{Period: 30, Skew: 1, Digits: otp.DigitsSix, Algorithm: otp.AlgorithmSHA256} + if valid, _ := totp.ValidateCustom(code, secret, time.Now(), opts); !valid { + err := store.DB.Transaction(func(tx *gorm.DB) error { + if failedTime + APPMFAFailPeriod > curTime { + if res := tx.Clauses(clause.OnConflict{ + Columns: []clause.Column{{Name: "config_id"}}, + DoUpdates: clause.AssignmentColumns([]string{"num_value"}), + }).Create(&store.Config{ConfigID: CNFMFAFailedCount, NumValue: failedCount + 1}).Error; res != nil { + return res + } + } else { + if res := tx.Clauses(clause.OnConflict{ + Columns: []clause.Column{{Name: "config_id"}}, + DoUpdates: clause.AssignmentColumns([]string{"num_value"}), + }).Create(&store.Config{ConfigID: CNFMFAFailedTime, NumValue: curTime}).Error; res != nil { + return res + } + if res := tx.Clauses(clause.OnConflict{ + Columns: []clause.Column{{Name: "config_id"}}, + DoUpdates: clause.AssignmentColumns([]string{"num_value"}), + }).Create(&store.Config{ConfigID: CNFMFAFailedCount, NumValue: 1}).Error; res != nil { + return res + } } + return nil + }) + if err != nil { + LogMsg("failed to increment fail count"); } - return nil - }) - if err != nil { - LogMsg("failed to increment fail count"); - } - ErrResponse(w, http.StatusUnauthorized, errors.New("invalid code")) - return + ErrResponse(w, http.StatusUnauthorized, errors.New("invalid code")) + return + } } err := store.DB.Transaction(func(tx *gorm.DB) error { @@ -81,6 +86,13 @@ func SetAdminMFAuth(w http.ResponseWriter, r *http.Request) { }).Create(&store.Config{ConfigID: CNFMFAConfirmed, BoolValue: true}).Error; res != nil { return res } + // upsert mfa algorithm + if res := tx.Clauses(clause.OnConflict{ + Columns: []clause.Column{{Name: "config_id"}}, + DoUpdates: clause.AssignmentColumns([]string{"num_value"}), + }).Create(&store.Config{ConfigID: CNFMFAAlgorithm, StrValue: mfaAlgorithm}).Error; res != nil { + return res + } return nil }) if err != nil { diff --git a/net/server/internal/api_setMultiFactorAuth.go b/net/server/internal/api_setMultiFactorAuth.go index 2c8cf68f..6c656f1a 100644 --- a/net/server/internal/api_setMultiFactorAuth.go +++ b/net/server/internal/api_setMultiFactorAuth.go @@ -35,32 +35,37 @@ func SetMultiFactorAuth(w http.ResponseWriter, r *http.Request) { return; } - opts := totp.ValidateOpts{Period: 30, Skew: 1, Digits: otp.DigitsSix, Algorithm: otp.AlgorithmSHA256} + mfaAlgorithm := APPMFASHA1 + opts := totp.ValidateOpts{Period: 30, Skew: 1, Digits: otp.DigitsSix, Algorithm: otp.AlgorithmSHA1} if valid, _ := totp.ValidateCustom(code, account.MFASecret, time.Now(), opts); !valid { - err := store.DB.Transaction(func(tx *gorm.DB) error { - if account.MFAFailedTime + APPMFAFailPeriod > curTime { - account.MFAFailedCount += 1 - if res := tx.Model(account).Update("mfa_failed_count", account.MFAFailedCount).Error; res != nil { - return res - } - } else { - account.MFAFailedTime = curTime - if res := tx.Model(account).Update("mfa_failed_time", account.MFAFailedTime).Error; res != nil { - return res - } - account.MFAFailedCount = 1 - if res := tx.Model(account).Update("mfa_failed_count", account.MFAFailedCount).Error; res != nil { - return res + mfaAlgorithm = APPMFASHA256 + opts := totp.ValidateOpts{Period: 30, Skew: 1, Digits: otp.DigitsSix, Algorithm: otp.AlgorithmSHA256} + if valid, _ := totp.ValidateCustom(code, account.MFASecret, time.Now(), opts); !valid { + err := store.DB.Transaction(func(tx *gorm.DB) error { + if account.MFAFailedTime + APPMFAFailPeriod > curTime { + account.MFAFailedCount += 1 + if res := tx.Model(account).Update("mfa_failed_count", account.MFAFailedCount).Error; res != nil { + return res + } + } else { + account.MFAFailedTime = curTime + if res := tx.Model(account).Update("mfa_failed_time", account.MFAFailedTime).Error; res != nil { + return res + } + account.MFAFailedCount = 1 + if res := tx.Model(account).Update("mfa_failed_count", account.MFAFailedCount).Error; res != nil { + return res + } } + return nil + }) + if err != nil { + LogMsg("failed to increment fail count"); } - return nil - }) - if err != nil { - LogMsg("failed to increment fail count"); - } - ErrResponse(w, http.StatusUnauthorized, errors.New("invalid code")) - return + ErrResponse(w, http.StatusUnauthorized, errors.New("invalid code")) + return + } } err = store.DB.Transaction(func(tx *gorm.DB) error { @@ -69,6 +74,11 @@ func SetMultiFactorAuth(w http.ResponseWriter, r *http.Request) { ErrResponse(w, http.StatusInternalServerError, res) return res } + account.MFAAlgorithm = mfaAlgorithm; + if res := tx.Model(account).Update("mfa_algorithm", account.MFAAlgorithm).Error; res != nil { + ErrResponse(w, http.StatusInternalServerError, res) + return res + } account.AccountRevision += 1; if res := tx.Model(&account).Update("account_revision", account.AccountRevision).Error; res != nil { return res diff --git a/net/server/internal/appValues.go b/net/server/internal/appValues.go index e16eae20..2701ae8e 100644 --- a/net/server/internal/appValues.go +++ b/net/server/internal/appValues.go @@ -156,6 +156,12 @@ const APPMFAFailPeriod = 300 //APPMFAFailCount limit of login failures in period const APPMFAFailCount = 4 +//APPMFASHA256 internal mfa algorithm sha256 +const APPMFASHA256 = "sha256" + +//APPMFASHA1 internal mfa alogirthm sha1 +const APPMFASHA1 = "sha1" + //AppCardStatus compares cards status with string func AppCardStatus(status string) bool { if status == APPCardPending { diff --git a/net/server/internal/configUtil.go b/net/server/internal/configUtil.go index 3502e705..7a0f2ed6 100644 --- a/net/server/internal/configUtil.go +++ b/net/server/internal/configUtil.go @@ -78,6 +78,9 @@ const CNFMFAEnabled = "mfa_enabled" //CNFMFAConfirmed specified if mfa has been confirmed for admin const CNFMFAConfirmed = "mfa_confirmed" +//CNFMFAAlgorirthm specifies internal mfa alogirhtm to use +const CNFMFAAlgorithm = "mfa_algorithm" + //CNFMFASecret specified the mfa secret const CNFMFASecret = "mfa_secret" diff --git a/net/server/internal/store/schema.go b/net/server/internal/store/schema.go index 3e095913..334fc41b 100644 --- a/net/server/internal/store/schema.go +++ b/net/server/internal/store/schema.go @@ -84,6 +84,7 @@ type Account struct { MFAEnabled bool `gorm:"not null;default:false"` MFAConfirmed bool `gorm:"not null;default:false"` MFASecret string + MFAAlgorithm string MFAFailedTime int64 MFAFailedCount uint Forward string diff --git a/net/web/src/constants/Strings.js b/net/web/src/constants/Strings.js index 93d7b7e8..1f729be8 100644 --- a/net/web/src/constants/Strings.js +++ b/net/web/src/constants/Strings.js @@ -193,7 +193,7 @@ export const en = { securedMessage: 'Sealed Message', mfaTitle: 'Multi-Factor Authentication', - mfaSteps: 'Store the SHA256 secret and confirm the verification code', + mfaSteps: 'Store the secret and confirm the verification code', mfaError: 'verification code error', mfaDisabled: 'verification temporarily disabled', mfaConfirm: 'Confirm', @@ -403,7 +403,7 @@ export const fr = { sealedMessage: 'Message Sécurisé', mfaTitle: 'Authentification Multi-Factor', - mfaSteps: 'Enregistrez le secret SHA256 et confirmez le code de vérification', + mfaSteps: 'Enregistrez le secret et confirmez le code de vérification', mfaEnter: 'Entrez votre code de vérification', mfaError: 'erreur de code de vérification', mfaDisabled: 'vérification temporairement désactivée', @@ -612,7 +612,7 @@ export const sp = { sealedMessage: 'Mensaje Seguro', mfaTitle: 'Autenticación de Dos Factores', - mfaSteps: 'Guarde el secreto SHA256 y confirme el código de verificación', + mfaSteps: 'Guarde el secreto y confirme el código de verificación', mfaEnter: 'Ingresa tu código de verificación', mfaError: 'error de código de verificación', mfaDisabled: 'verificación temporalmente deshabilitada', @@ -821,7 +821,7 @@ export const pt = { sealedMessage: 'Mensagem Segura', mfaTitle: 'Autenticação de Dois Fatores', - mfaSteps: 'Salve o segredo SHA256 e confirme o código de verificação', + mfaSteps: 'Salve o segredo e confirme o código de verificação', mfaEnter: 'Digite seu código de verificação', mfaError: 'erro de código de verificação', mfaDisabled: 'verificação temporariamente desativada', @@ -1030,7 +1030,7 @@ export const de = { sealedMessage: 'Gesicherte Nachricht', mfaTitle: 'Zwei-Faktor-Authentifizierung', - mfaSteps: 'Speichern Sie das SHA256-Geheimnis und bestätigen Sie den Bestätigungscode', + mfaSteps: 'Speichern Sie das Geheimnis und bestätigen Sie den Bestätigungscode', mfaEnter: 'Geben Sie Ihren Bestätigungs-Code ein', mfaError: 'Verifizierungscodefehler', mfaDisabled: 'Verifizierung vorübergehend deaktiviert', @@ -1239,7 +1239,7 @@ export const ru = { sealedMessage: 'Защищенное Cообщение', mfaTitle: 'Двухфакторная аутентификация', - mfaSteps: 'Сохраните секрет SHA256 и подтвердите код подтверждения', + mfaSteps: 'Сохраните секрет и подтвердите код подтверждения', mfaEnter: 'Введите Ваш верификационный код', mfaError: 'ошибка проверочного кода', mfaDisabled: 'проверка временно отключена',