From 1c36b2958d30964e1510bcedebb4309467e00073 Mon Sep 17 00:00:00 2001 From: Stephen Heindel Date: Tue, 10 May 2022 23:45:54 -0400 Subject: [PATCH 1/5] Added URL Safe Base64 Data Encoder --- NBitcoin/DataEncoders/Base64UrlSafeEncoder.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 NBitcoin/DataEncoders/Base64UrlSafeEncoder.cs diff --git a/NBitcoin/DataEncoders/Base64UrlSafeEncoder.cs b/NBitcoin/DataEncoders/Base64UrlSafeEncoder.cs new file mode 100644 index 0000000000..88d7fcb57f --- /dev/null +++ b/NBitcoin/DataEncoders/Base64UrlSafeEncoder.cs @@ -0,0 +1,18 @@ +using System; + +namespace NBitcoin.DataEncoders +{ + public class Base64UrlSafeEncoder : DataEncoder + { + static readonly char[] padding = { '=' }; + public override byte[] DecodeData(string encoded) + { + return Convert.FromBase64String(encoded.TrimEnd(padding).Replace('-', '+').Replace('_', '/')); + } + + public override string EncodeData(byte[] data, int offset, int count) + { + return Convert.ToBase64String(data, offset, count).Replace("+", "-").Replace("/", "_"); + } + } +} From 2b578352b10d10cc107c742882ef95a3a4d841eb Mon Sep 17 00:00:00 2001 From: Stephen Heindel Date: Wed, 11 May 2022 02:58:37 -0400 Subject: [PATCH 2/5] Refactor unit tests to deal with raw bytes --- NBitcoin.Tests/ConverterTests.cs | 38 +++++++++++++++++-- NBitcoin/DataEncoders/Base64UrlSafeEncoder.cs | 14 ++++++- NBitcoin/DataEncoders/Encoders.cs | 9 +++++ 3 files changed, 55 insertions(+), 6 deletions(-) diff --git a/NBitcoin.Tests/ConverterTests.cs b/NBitcoin.Tests/ConverterTests.cs index c92b31b20d..2d305e623e 100644 --- a/NBitcoin.Tests/ConverterTests.cs +++ b/NBitcoin.Tests/ConverterTests.cs @@ -15,7 +15,12 @@ public class ConverterTests [Trait("UnitTest", "UnitTest")] public void CanConvertText() { - string testPhrase = "é ^ç hello \"12345\" wooorld"; + var testPhrase = Encoding.UTF8.GetBytes("é ^ç hello \"12345\" wooorld"); + var rfc4648_1 = new byte[] { 0x14, 0xfb, 0x9c, 0x03, 0xd9, 0x7e }; + var rfc4648_2 = new byte[] { 0x14, 0xfb, 0x9c, 0x03, 0xd9 }; + var rfc4648_3 = new byte[] { 0x14, 0xfb, 0x9c, 0x03 }; + + var tests = new[] { new @@ -43,6 +48,31 @@ public void CanConvertText() Input = testPhrase, Expected = "w6kgXsOnIGhlbGxvICIxMjM0NSIgIHdvb29ybGQ=" }, + new + { + Encoder = Encoders.Base64UrlSafe, + Input = testPhrase, + Expected = "w6kgXsOnIGhlbGxvICIxMjM0NSIgIHdvb29ybGQ" + }, + new + { + Encoder = Encoders.Base64UrlSafe, + Input = rfc4648_1, + Expected = "FPucA9l-" + }, + new + { + Encoder = Encoders.Base64UrlSafe, + Input = rfc4648_2, + Expected = "FPucA9k" + }, + new + { + Encoder = Encoders.Base64UrlSafe, + Input = rfc4648_3, + Expected = "FPucAw" + }, + //Not yet implemented //new //{ @@ -89,14 +119,14 @@ public void CanConvertText() foreach (var test in tests) { - var input = Encoding.UTF8.GetBytes(test.Input); - var encoded = test.Encoder.EncodeData(input); + //var input = Encoding.UTF8.GetBytes(test.Input); + var encoded = test.Encoder.EncodeData(test.Input); Assert.Equal(test.Expected, encoded); try { var decoded = test.Encoder.DecodeData(encoded); - AssertEx.CollectionEquals(input, decoded); + AssertEx.CollectionEquals(test.Input, decoded); } catch (NotSupportedException) { diff --git a/NBitcoin/DataEncoders/Base64UrlSafeEncoder.cs b/NBitcoin/DataEncoders/Base64UrlSafeEncoder.cs index 88d7fcb57f..8eb766cad5 100644 --- a/NBitcoin/DataEncoders/Base64UrlSafeEncoder.cs +++ b/NBitcoin/DataEncoders/Base64UrlSafeEncoder.cs @@ -1,18 +1,28 @@ using System; +using System.Linq; namespace NBitcoin.DataEncoders { public class Base64UrlSafeEncoder : DataEncoder { static readonly char[] padding = { '=' }; + static readonly int paddingBoundary = 4; public override byte[] DecodeData(string encoded) { - return Convert.FromBase64String(encoded.TrimEnd(padding).Replace('-', '+').Replace('_', '/')); + var temp = encoded.Replace('-', '+').Replace('_', '/'); + var remaining = temp.Length % paddingBoundary; + switch(remaining) { + case 2: + case 3: + temp += string.Concat(Enumerable.Repeat('=', paddingBoundary-remaining)); + break; + } + return Convert.FromBase64String(temp); } public override string EncodeData(byte[] data, int offset, int count) { - return Convert.ToBase64String(data, offset, count).Replace("+", "-").Replace("/", "_"); + return Convert.ToBase64String(data, offset, count).Replace("+", "-").Replace("/", "_").TrimEnd(padding); } } } diff --git a/NBitcoin/DataEncoders/Encoders.cs b/NBitcoin/DataEncoders/Encoders.cs index a98b8f5422..bde1d79304 100644 --- a/NBitcoin/DataEncoders/Encoders.cs +++ b/NBitcoin/DataEncoders/Encoders.cs @@ -98,6 +98,15 @@ public static DataEncoder Base64 } } + static readonly Base64UrlSafeEncoder _Base64UrlSafe = new Base64UrlSafeEncoder(); + public static DataEncoder Base64UrlSafe + { + get + { + return _Base64UrlSafe; + } + } + public static Bech32Encoder Bech32(string hrp) { return new Bech32Encoder(hrp); From 243c99bf2a17d8e78af1efdc9909a116c7f371d6 Mon Sep 17 00:00:00 2001 From: Stephen Heindel Date: Wed, 11 May 2022 02:59:13 -0400 Subject: [PATCH 3/5] Cleanup code --- NBitcoin.Tests/ConverterTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/NBitcoin.Tests/ConverterTests.cs b/NBitcoin.Tests/ConverterTests.cs index 2d305e623e..ec385933d5 100644 --- a/NBitcoin.Tests/ConverterTests.cs +++ b/NBitcoin.Tests/ConverterTests.cs @@ -119,7 +119,6 @@ public void CanConvertText() foreach (var test in tests) { - //var input = Encoding.UTF8.GetBytes(test.Input); var encoded = test.Encoder.EncodeData(test.Input); Assert.Equal(test.Expected, encoded); From a86fc78012eaacf182a7040bc6089dba3e3ffc07 Mon Sep 17 00:00:00 2001 From: Stephen Heindel Date: Wed, 11 May 2022 03:01:53 -0400 Subject: [PATCH 4/5] Added code comment for padding re-add --- NBitcoin/DataEncoders/Base64UrlSafeEncoder.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NBitcoin/DataEncoders/Base64UrlSafeEncoder.cs b/NBitcoin/DataEncoders/Base64UrlSafeEncoder.cs index 8eb766cad5..7e1e7ce647 100644 --- a/NBitcoin/DataEncoders/Base64UrlSafeEncoder.cs +++ b/NBitcoin/DataEncoders/Base64UrlSafeEncoder.cs @@ -10,6 +10,8 @@ public class Base64UrlSafeEncoder : DataEncoder public override byte[] DecodeData(string encoded) { var temp = encoded.Replace('-', '+').Replace('_', '/'); + + // Re-add padding if necessary var remaining = temp.Length % paddingBoundary; switch(remaining) { case 2: From 53137f0b9c0c43e9345acbab894f381cf0a04cf6 Mon Sep 17 00:00:00 2001 From: Stephen Heindel Date: Wed, 11 May 2022 03:02:57 -0400 Subject: [PATCH 5/5] Convert replace call to character usage --- NBitcoin/DataEncoders/Base64UrlSafeEncoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NBitcoin/DataEncoders/Base64UrlSafeEncoder.cs b/NBitcoin/DataEncoders/Base64UrlSafeEncoder.cs index 7e1e7ce647..fd50013c23 100644 --- a/NBitcoin/DataEncoders/Base64UrlSafeEncoder.cs +++ b/NBitcoin/DataEncoders/Base64UrlSafeEncoder.cs @@ -24,7 +24,7 @@ public override byte[] DecodeData(string encoded) public override string EncodeData(byte[] data, int offset, int count) { - return Convert.ToBase64String(data, offset, count).Replace("+", "-").Replace("/", "_").TrimEnd(padding); + return Convert.ToBase64String(data, offset, count).Replace('+', '-').Replace('/', '_').TrimEnd(padding); } } }