From 54c4736965fc2f8c714c9d6b103b2ac9f2b448f9 Mon Sep 17 00:00:00 2001 From: Josh Parnham Date: Tue, 8 Mar 2022 15:00:32 +1100 Subject: [PATCH 1/3] Ensure that an OTP's issuer is correctly escaped when it contains a space --- QRCoder/PayloadGenerator.cs | 4 ++-- QRCoderTests/PayloadGeneratorTests.cs | 31 +++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/QRCoder/PayloadGenerator.cs b/QRCoder/PayloadGenerator.cs index 6275e5be..aa84f2e9 100644 --- a/QRCoder/PayloadGenerator.cs +++ b/QRCoder/PayloadGenerator.cs @@ -2030,11 +2030,11 @@ private void ProcessCommonFields(StringBuilder sb) if (Label != null && Issuer != null) { - label = Issuer + ":" + Label; + label = escapedIssuer + ":" + Label; } else if (Issuer != null) { - label = Issuer; + label = escapedIssuer; } if (label != null) diff --git a/QRCoderTests/PayloadGeneratorTests.cs b/QRCoderTests/PayloadGeneratorTests.cs index f4406bb7..236d74e4 100644 --- a/QRCoderTests/PayloadGeneratorTests.cs +++ b/QRCoderTests/PayloadGeneratorTests.cs @@ -2660,6 +2660,21 @@ public void one_time_password_generator_time_based_generates_with_standard_optio } + [Fact] + [Category("PayloadGenerator/OneTimePassword")] + public void one_time_password_generator_time_based_generates_with_standard_options_escapes_issuer() + { + var pg = new PayloadGenerator.OneTimePassword + { + Secret = "pwq6 5q55", + Issuer = "Google Google", + Label = "test@google.com", + }; + + pg.ToString().ShouldBe("otpauth://totp/Google%20Google:test@google.com?secret=pwq65q55&issuer=Google%20Google"); + } + + [Fact] [Category("PayloadGenerator/OneTimePassword")] public void one_time_password_generator_hmac_based_generates_with_standard_options() @@ -2676,6 +2691,22 @@ public void one_time_password_generator_hmac_based_generates_with_standard_optio pg.ToString().ShouldBe("otpauth://hotp/Google:test@google.com?secret=pwq65q55&issuer=Google&counter=500"); } + [Fact] + [Category("PayloadGenerator/OneTimePassword")] + public void one_time_password_generator_hmac_based_generates_with_standard_options_escapes_issuer() + { + var pg = new PayloadGenerator.OneTimePassword + { + Secret = "pwq6 5q55", + Issuer = "Google Google", + Label = "test@google.com", + Type = PayloadGenerator.OneTimePassword.OneTimePasswordAuthType.HOTP, + Counter = 500, + }; + + pg.ToString().ShouldBe("otpauth://hotp/Google%20Google:test@google.com?secret=pwq65q55&issuer=Google%20Google&counter=500"); + } + [Fact] [Category("PayloadGenerator/ShadowSocksConfig")] From 32c4aa02402a489519e9e649ecf1d84fdcf755b8 Mon Sep 17 00:00:00 2001 From: Josh Parnham Date: Tue, 15 Mar 2022 16:02:02 +1100 Subject: [PATCH 2/3] Check escapedIssuer instead of Issuer --- QRCoder/PayloadGenerator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/QRCoder/PayloadGenerator.cs b/QRCoder/PayloadGenerator.cs index aa84f2e9..95af78c9 100644 --- a/QRCoder/PayloadGenerator.cs +++ b/QRCoder/PayloadGenerator.cs @@ -2028,11 +2028,11 @@ private void ProcessCommonFields(StringBuilder sb) throw new Exception("Label must not have a ':'"); } - if (Label != null && Issuer != null) + if (Label != null && escapedIssuer != null) { label = escapedIssuer + ":" + Label; } - else if (Issuer != null) + else if (escapedIssuer != null) { label = escapedIssuer; } From 0fd25bc19d37389b9057867a00cbae141ef05569 Mon Sep 17 00:00:00 2001 From: Josh Parnham Date: Tue, 15 Mar 2022 16:11:00 +1100 Subject: [PATCH 3/3] Also escape label --- QRCoder/PayloadGenerator.cs | 13 +++++++++---- QRCoderTests/PayloadGeneratorTests.cs | 16 ++++++++-------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/QRCoder/PayloadGenerator.cs b/QRCoder/PayloadGenerator.cs index 95af78c9..48336a73 100644 --- a/QRCoder/PayloadGenerator.cs +++ b/QRCoder/PayloadGenerator.cs @@ -2012,6 +2012,7 @@ private void ProcessCommonFields(StringBuilder sb) } string strippedSecret = Secret.Replace(" ", ""); string escapedIssuer = null; + string escapedLabel = null; string label = null; if (!String40Methods.IsNullOrWhiteSpace(Issuer)) @@ -2023,14 +2024,18 @@ private void ProcessCommonFields(StringBuilder sb) escapedIssuer = Uri.EscapeDataString(Issuer); } - if (!String40Methods.IsNullOrWhiteSpace(Label) && Label.Contains(":")) + if (!String40Methods.IsNullOrWhiteSpace(Label)) { - throw new Exception("Label must not have a ':'"); + if (Label.Contains(":")) + { + throw new Exception("Label must not have a ':'"); + } + escapedLabel = Uri.EscapeDataString(Label); } - if (Label != null && escapedIssuer != null) + if (escapedLabel != null && escapedIssuer != null) { - label = escapedIssuer + ":" + Label; + label = escapedIssuer + ":" + escapedLabel; } else if (escapedIssuer != null) { diff --git a/QRCoderTests/PayloadGeneratorTests.cs b/QRCoderTests/PayloadGeneratorTests.cs index 236d74e4..2e90e3ce 100644 --- a/QRCoderTests/PayloadGeneratorTests.cs +++ b/QRCoderTests/PayloadGeneratorTests.cs @@ -2656,22 +2656,22 @@ public void one_time_password_generator_time_based_generates_with_standard_optio Label = "test@google.com", }; - pg.ToString().ShouldBe("otpauth://totp/Google:test@google.com?secret=pwq65q55&issuer=Google"); + pg.ToString().ShouldBe("otpauth://totp/Google:test%40google.com?secret=pwq65q55&issuer=Google"); } [Fact] [Category("PayloadGenerator/OneTimePassword")] - public void one_time_password_generator_time_based_generates_with_standard_options_escapes_issuer() + public void one_time_password_generator_time_based_generates_with_standard_options_escapes_issuer_and_label() { var pg = new PayloadGenerator.OneTimePassword { Secret = "pwq6 5q55", Issuer = "Google Google", - Label = "test@google.com", + Label = "test/test@google.com", }; - pg.ToString().ShouldBe("otpauth://totp/Google%20Google:test@google.com?secret=pwq65q55&issuer=Google%20Google"); + pg.ToString().ShouldBe("otpauth://totp/Google%20Google:test%2Ftest%40google.com?secret=pwq65q55&issuer=Google%20Google"); } @@ -2688,23 +2688,23 @@ public void one_time_password_generator_hmac_based_generates_with_standard_optio Counter = 500, }; - pg.ToString().ShouldBe("otpauth://hotp/Google:test@google.com?secret=pwq65q55&issuer=Google&counter=500"); + pg.ToString().ShouldBe("otpauth://hotp/Google:test%40google.com?secret=pwq65q55&issuer=Google&counter=500"); } [Fact] [Category("PayloadGenerator/OneTimePassword")] - public void one_time_password_generator_hmac_based_generates_with_standard_options_escapes_issuer() + public void one_time_password_generator_hmac_based_generates_with_standard_options_escapes_issuer_and_label() { var pg = new PayloadGenerator.OneTimePassword { Secret = "pwq6 5q55", Issuer = "Google Google", - Label = "test@google.com", + Label = "test/test@google.com", Type = PayloadGenerator.OneTimePassword.OneTimePasswordAuthType.HOTP, Counter = 500, }; - pg.ToString().ShouldBe("otpauth://hotp/Google%20Google:test@google.com?secret=pwq65q55&issuer=Google%20Google&counter=500"); + pg.ToString().ShouldBe("otpauth://hotp/Google%20Google:test%2Ftest%40google.com?secret=pwq65q55&issuer=Google%20Google&counter=500"); }