From b6e4cf50a4c3c45fbd775b285a5f025c315aff64 Mon Sep 17 00:00:00 2001 From: Bubka <858858+Bubka@users.noreply.github.com> Date: Tue, 13 Dec 2022 09:05:56 +0100 Subject: [PATCH] Remove the ability to set a plain text secret --- app/Helpers/Helpers.php | 11 +++++++ app/Models/TwoFAccount.php | 2 +- app/Rules/IsBase32Encoded.php | 3 +- package-lock.json | 13 +------- package.json | 1 - resources/js/views/twofaccounts/Create.vue | 35 ++-------------------- resources/js/views/twofaccounts/Edit.vue | 17 ----------- resources/js/views/twofaccounts/Import.vue | 1 - 8 files changed, 17 insertions(+), 66 deletions(-) diff --git a/app/Helpers/Helpers.php b/app/Helpers/Helpers.php index acd23fc8..c0db245b 100644 --- a/app/Helpers/Helpers.php +++ b/app/Helpers/Helpers.php @@ -28,4 +28,15 @@ class Helpers // We use the regex for semver detection (see https://semver.org/) return preg_match('/(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?/', $release, $version) ? $version[0] : false; } + + /** + * Format a string to comply with Base32 format + * + * @param string $str + * @return string The filename + */ + public static function PadToBase32Format(?string $str): string + { + return blank($str) ? '' : strtoupper(str_pad($str, (int) ceil(strlen($str) / 8) * 8, '=')); + } } diff --git a/app/Models/TwoFAccount.php b/app/Models/TwoFAccount.php index 7a12361a..5349077e 100644 --- a/app/Models/TwoFAccount.php +++ b/app/Models/TwoFAccount.php @@ -242,7 +242,7 @@ class TwoFAccount extends Model implements Sortable public function setSecretAttribute($value) { // Encrypt when needed - $this->attributes['secret'] = $this->encryptOrReturn($value); + $this->attributes['secret'] = $this->encryptOrReturn(Helpers::PadToBase32Format($value)); } /** diff --git a/app/Rules/IsBase32Encoded.php b/app/Rules/IsBase32Encoded.php index 7be67770..c5becfb1 100644 --- a/app/Rules/IsBase32Encoded.php +++ b/app/Rules/IsBase32Encoded.php @@ -2,6 +2,7 @@ namespace App\Rules; +use App\Helpers\Helpers; use Illuminate\Contracts\Validation\Rule; use ParagonIE\ConstantTime\Base32; @@ -27,7 +28,7 @@ class IsBase32Encoded implements Rule public function passes($attribute, $value) { try { - $secret = Base32::decodeUpper($value); + $secret = Base32::decodeUpper(Helpers::PadToBase32Format($value)); } catch (\Exception $e) { return false; } diff --git a/package-lock.json b/package-lock.json index 6743822f..caca4d86 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,11 +9,10 @@ "@fortawesome/free-brands-svg-icons": "^5.15.4", "@fortawesome/free-solid-svg-icons": "^5.15.4", "@fortawesome/vue-fontawesome": "^2.0.6", - "axios": "^1.0.0", + "axios": "^1.1.0", "bulma": "^0.9.3", "bulma-checkradio": "^2.1.2", "bulma-switch": "^2.0.0", - "hi-base32": "^0.5.1", "object-equals": "^0.3.0", "v-clipboard": "^2.2.3", "vue": "^2.6.14", @@ -5116,11 +5115,6 @@ "he": "bin/he" } }, - "node_modules/hi-base32": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/hi-base32/-/hi-base32-0.5.1.tgz", - "integrity": "sha512-EmBBpvdYh/4XxsnUybsPag6VikPYnN30td+vQk+GI3qpahVEG9+gTkG0aXVxTjBqQ5T6ijbWIu77O+C5WFWsnA==" - }, "node_modules/hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", @@ -13647,11 +13641,6 @@ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, - "hi-base32": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/hi-base32/-/hi-base32-0.5.1.tgz", - "integrity": "sha512-EmBBpvdYh/4XxsnUybsPag6VikPYnN30td+vQk+GI3qpahVEG9+gTkG0aXVxTjBqQ5T6ijbWIu77O+C5WFWsnA==" - }, "hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", diff --git a/package.json b/package.json index 6961afc2..68f5e8e4 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,6 @@ "bulma": "^0.9.3", "bulma-checkradio": "^2.1.2", "bulma-switch": "^2.0.0", - "hi-base32": "^0.5.1", "object-equals": "^0.3.0", "v-clipboard": "^2.2.3", "vue": "^2.6.14", diff --git a/resources/js/views/twofaccounts/Create.vue b/resources/js/views/twofaccounts/Create.vue index 5c74be81..0481538c 100644 --- a/resources/js/views/twofaccounts/Create.vue +++ b/resources/js/views/twofaccounts/Create.vue @@ -101,14 +101,7 @@
-
-

- - - -

+

@@ -146,7 +139,7 @@ - + @@ -198,7 +191,6 @@ import Modal from '../../components/Modal' import Form from './../../components/Form' import OtpDisplayer from '../../components/OtpDisplayer' - import Base32 from "hi-base32" export default { data() { @@ -209,7 +201,6 @@ showAlternatives : false, tempIcon: '', uri: '', - secretIsBase32Encoded: 0, form: new Form({ service: '', account: '', @@ -235,10 +226,6 @@ { text: 9, value: 9 }, { text: 10, value: 10 }, ], - secretFormats: [ - { text: this.$t('twofaccounts.forms.plain_text'), value: 0 }, - { text: 'Base32', value: 1 } - ], algorithms: [ { text: 'sha1', value: 'sha1' }, { text: 'sha256', value: 'sha256' }, @@ -260,17 +247,6 @@ }, }, - computed: { - otpdisplayerData: function() { - let o = this.form.data() - o.secret = this.secretIsBase32Encoded - ? o.secret - : Base32.encode(o.secret).toString(); - - return o - } - }, - mounted: function () { if( this.$route.params.decodedUri ) { this.uri = this.$route.params.decodedUri @@ -279,7 +255,6 @@ this.axios.post('/api/v1/twofaccounts/preview', { uri: this.uri }).then(response => { this.form.fill(response.data) - this.secretIsBase32Encoded = 1 this.tempIcon = response.data.icon ? response.data.icon : null this.showQuickForm = true }) @@ -315,9 +290,6 @@ // set current temp icon as account icon this.form.icon = this.tempIcon - // Secret to base32 if necessary - this.form.secret = this.secretIsBase32Encoded ? this.form.secret : Base32.encode(this.form.secret).toString(); - await this.form.post('/api/v1/twofaccounts') if( this.form.errors.any() === false ) { @@ -359,7 +331,6 @@ // Then the otp described by the uri this.axios.post('/api/v1/twofaccounts/preview', { uri: this.uri }).then(response => { this.form.fill(response.data) - this.secretIsBase32Encoded = 1 this.tempIcon = response.data.icon ? response.data.icon : null }) .catch(error => { @@ -441,9 +412,7 @@ this.form.otp_type = to if (to === 'steamtotp') { - this.secretIsBase32Encoded = 1 this.form.service = 'Steam' - this.form.secret = '' this.fetchLogo() } else if (from === 'steamtotp') { diff --git a/resources/js/views/twofaccounts/Edit.vue b/resources/js/views/twofaccounts/Edit.vue index c92ca240..fb855fce 100644 --- a/resources/js/views/twofaccounts/Edit.vue +++ b/resources/js/views/twofaccounts/Edit.vue @@ -46,13 +46,6 @@
-

- - - -

@@ -141,7 +134,6 @@ import Modal from '../../components/Modal' import Form from './../../components/Form' import OtpDisplayer from '../../components/OtpDisplayer' - import Base32 from "hi-base32" export default { data() { @@ -150,7 +142,6 @@ counterIsLocked: true, twofaccountExists: false, tempIcon: '', - secretIsBase32Encoded: null, form: new Form({ service: '', account: '', @@ -176,10 +167,6 @@ { text: 9, value: 9 }, { text: 10, value: 10 }, ], - secretFormats: [ - { text: this.$t('twofaccounts.forms.plain_text'), value: 0 }, - { text: 'Base32', value: 1 } - ], algorithms: [ { text: 'sha1', value: 'sha1' }, { text: 'sha256', value: 'sha256' }, @@ -214,7 +201,6 @@ const { data } = await this.axios.get('/api/v1/twofaccounts/' + this.$route.params.twofaccountId) this.form.fill(data) - this.secretIsBase32Encoded = 1 this.twofaccountExists = true // set account icon as temp icon @@ -235,9 +221,6 @@ this.deleteIcon() } - // Secret to base32 if necessary - this.form.secret = this.secretIsBase32Encoded ? this.form.secret : Base32.encode(this.form.secret).toString(); - await this.form.put('/api/v1/twofaccounts/' + this.$route.params.twofaccountId) if( this.form.errors.any() === false ) { diff --git a/resources/js/views/twofaccounts/Import.vue b/resources/js/views/twofaccounts/Import.vue index 49ad38f8..32e1b28e 100644 --- a/resources/js/views/twofaccounts/Import.vue +++ b/resources/js/views/twofaccounts/Import.vue @@ -171,7 +171,6 @@ otp_type: '', icon: '', secret: '', - secretIsBase32Encoded: 1, algorithm: '', digits: null, counter: null,