55// Licensed under MIT license (https://opensource.org/licenses/MIT)
66// ------------------------------------------------------------
77
8- using System . Collections . Generic ;
8+ using System ;
9+ using System . Linq ;
910
1011namespace SimpleOTP . Helpers
1112{
@@ -14,6 +15,7 @@ namespace SimpleOTP.Helpers
1415 /// </summary>
1516 internal static class Base32Encoder
1617 {
18+ // Standard RFC 4648 Base32 alphabet
1719 private const string AllowedCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" ;
1820
1921 /// <summary>
@@ -23,16 +25,20 @@ internal static class Base32Encoder
2325 /// <returns>Base32 string.</returns>
2426 internal static string Encode ( byte [ ] data )
2527 {
26- // FIXME: Encoder works correctly only with 160-bit keys
28+ string binary = string . Empty ;
29+ foreach ( byte b in data )
30+ binary += Convert . ToString ( b , 2 ) . PadLeft ( 8 , '0' ) ; // Getting binary sequence to split into 5 digits
31+
32+ int numberOfBlocks = ( binary . Length / 5 ) + Math . Clamp ( binary . Length % 5 , 0 , 1 ) ;
33+ string [ ] sequence = Enumerable . Range ( 0 , numberOfBlocks )
34+ . Select ( i => binary . Substring ( i * 5 , Math . Min ( 5 , binary . Length - ( i * 5 ) ) ) . PadRight ( 5 , '0' ) )
35+ . ToArray ( ) ; // Splitting sequence on groups of 5
36+
2737 string output = string . Empty ;
28- for ( int bitIndex = 0 ; bitIndex < data . Length * 8 ; bitIndex += 5 )
29- {
30- int dualbyte = data [ bitIndex / 8 ] << 8 ;
31- if ( ( bitIndex / 8 ) + 1 < data . Length )
32- dualbyte |= data [ ( bitIndex / 8 ) + 1 ] ;
33- dualbyte = 0x1f & ( dualbyte >> ( 16 - ( bitIndex % 8 ) - 5 ) ) ;
34- output += AllowedCharacters [ dualbyte ] ;
35- }
38+ foreach ( string str in sequence )
39+ output += AllowedCharacters [ Convert . ToInt32 ( str , 2 ) ] ;
40+
41+ output = output . PadRight ( output . Length + ( output . Length % 8 ) , '=' ) ;
3642
3743 return output ;
3844 }
@@ -44,21 +50,14 @@ internal static string Encode(byte[] data)
4450 /// <returns>Initial byte array.</returns>
4551 internal static byte [ ] Decode ( string base32str )
4652 {
47- List < byte > output = new ( ) ;
48- char [ ] bytes = base32str . ToCharArray ( ) ;
49- for ( var bitIndex = 0 ; bitIndex < base32str . Length * 5 ; bitIndex += 8 )
50- {
51- var dualbyte = AllowedCharacters . IndexOf ( bytes [ bitIndex / 5 ] ) << 10 ;
52- if ( ( bitIndex / 5 ) + 1 < bytes . Length )
53- dualbyte |= AllowedCharacters . IndexOf ( bytes [ ( bitIndex / 5 ) + 1 ] ) << 5 ;
54- if ( ( bitIndex / 5 ) + 2 < bytes . Length )
55- dualbyte |= AllowedCharacters . IndexOf ( bytes [ ( bitIndex / 5 ) + 2 ] ) ;
53+ base32str = base32str . Replace ( "=" , string . Empty ) ; // Removing padding
5654
57- dualbyte = 0xff & ( dualbyte >> ( 15 - ( bitIndex % 5 ) - 8 ) ) ;
58- output . Add ( ( byte ) dualbyte ) ;
59- }
55+ string [ ] quintets = base32str . Select ( i => Convert . ToString ( AllowedCharacters . IndexOf ( i ) , 2 ) . PadLeft ( 5 , '0' ) ) . ToArray ( ) ; // Getting quintets
56+ string binary = string . Join ( null , quintets ) ;
6057
61- return output . ToArray ( ) ;
58+ byte [ ] output = Enumerable . Range ( 0 , binary . Length / 8 ) . Select ( i => Convert . ToByte ( binary . Substring ( i * 8 , 8 ) , 2 ) ) . ToArray ( ) ;
59+
60+ return output ;
6261 }
6362 }
6463}
0 commit comments