diff --git a/NBitcoin.Tests/ConverterTests.cs b/NBitcoin.Tests/ConverterTests.cs index c92b31b20d..ec385933d5 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,13 @@ public void CanConvertText() foreach (var test in tests) { - var input = Encoding.UTF8.GetBytes(test.Input); - var encoded = test.Encoder.EncodeData(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 new file mode 100644 index 0000000000..fd50013c23 --- /dev/null +++ b/NBitcoin/DataEncoders/Base64UrlSafeEncoder.cs @@ -0,0 +1,30 @@ +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) + { + var temp = encoded.Replace('-', '+').Replace('_', '/'); + + // Re-add padding if necessary + 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('/', '_').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);