@@ -76,29 +76,36 @@ public static void RoundtripWithPadding2()
7676 [ Fact ]
7777 public static void PartialRoundtripWithPadding1 ( )
7878 {
79+ // "ab==" has non-zero unused bits and should be rejected per RFC 4648
80+ // The valid encoding for the same byte should be "aQ=="
7981 string input = "ab==" ;
80- Verify ( input , result =>
82+ VerifyInvalidInput ( input ) ;
83+
84+ // Test the valid encoding instead
85+ string validInput = "aQ==" ;
86+ Verify ( validInput , result =>
8187 {
8288 Assert . Equal ( 1 , result . Length ) ;
83-
8489 string roundtrippedString = Convert . ToBase64String ( result ) ;
85- Assert . NotEqual ( input , roundtrippedString ) ;
86- Assert . Equal ( input [ 0 ] , roundtrippedString [ 0 ] ) ;
90+ Assert . Equal ( validInput , roundtrippedString ) ;
8791 } ) ;
8892 }
8993
9094 [ Fact ]
9195 public static void PartialRoundtripWithPadding2 ( )
9296 {
97+ // "789=" has non-zero unused bits and should be rejected per RFC 4648
98+ // The valid encoding for the same 2 bytes should be "788="
9399 string input = "789=" ;
94- Verify ( input , result =>
100+ VerifyInvalidInput ( input ) ;
101+
102+ // Test the valid encoding instead
103+ string validInput = "788=" ;
104+ Verify ( validInput , result =>
95105 {
96106 Assert . Equal ( 2 , result . Length ) ;
97-
98107 string roundtrippedString = Convert . ToBase64String ( result ) ;
99- Assert . NotEqual ( input , roundtrippedString ) ;
100- Assert . Equal ( input [ 0 ] , roundtrippedString [ 0 ] ) ;
101- Assert . Equal ( input [ 1 ] , roundtrippedString [ 1 ] ) ;
108+ Assert . Equal ( validInput , roundtrippedString ) ;
102109 } ) ;
103110 }
104111
@@ -266,5 +273,71 @@ private static void Verify(string input, Action<byte[]> action = null)
266273 action ( Convert . FromBase64String ( input ) ) ;
267274 }
268275 }
276+
277+ [ Fact ]
278+ public static void RejectsInvalidUnusedBits_OnePadding ( )
279+ {
280+ // When there's one padding character (2 bytes output), the last 2 bits must be 0
281+ // "QUI=" is valid (encodes "AB"), but variations with non-zero unused bits should fail
282+ string validInput = "QUI=" ;
283+ byte [ ] result = Convert . FromBase64String ( validInput ) ;
284+ Assert . Equal ( 2 , result . Length ) ;
285+ Assert . Equal ( 65 , result [ 0 ] ) ; // 'A'
286+ Assert . Equal ( 66 , result [ 1 ] ) ; // 'B'
287+
288+ // These have non-zero unused bits and should be rejected
289+ VerifyInvalidInput ( "QUJ=" ) ; // last 2 bits != 0
290+ VerifyInvalidInput ( "QUK=" ) ; // last 2 bits != 0
291+ VerifyInvalidInput ( "QUL=" ) ; // last 2 bits != 0
292+ }
293+
294+ [ Fact ]
295+ public static void RejectsInvalidUnusedBits_TwoPadding ( )
296+ {
297+ // When there are two padding characters (1 byte output), the last 4 bits must be 0
298+ // "QQ==" is valid (encodes "A"), but variations with non-zero unused bits should fail
299+ string validInput = "QQ==" ;
300+ byte [ ] result = Convert . FromBase64String ( validInput ) ;
301+ Assert . Equal ( 1 , result . Length ) ;
302+ Assert . Equal ( 65 , result [ 0 ] ) ; // 'A'
303+
304+ // These have non-zero unused bits and should be rejected
305+ VerifyInvalidInput ( "QR==" ) ; // last 4 bits != 0
306+ VerifyInvalidInput ( "QS==" ) ; // last 4 bits != 0
307+ VerifyInvalidInput ( "QT==" ) ; // last 4 bits != 0
308+ VerifyInvalidInput ( "QU==" ) ; // last 4 bits != 0
309+ VerifyInvalidInput ( "QV==" ) ; // last 4 bits != 0
310+ VerifyInvalidInput ( "QW==" ) ; // last 4 bits != 0
311+ VerifyInvalidInput ( "QX==" ) ; // last 4 bits != 0
312+ VerifyInvalidInput ( "QY==" ) ; // last 4 bits != 0
313+ VerifyInvalidInput ( "QZ==" ) ; // last 4 bits != 0
314+ VerifyInvalidInput ( "Qa==" ) ; // last 4 bits != 0
315+ VerifyInvalidInput ( "Qb==" ) ; // last 4 bits != 0
316+ VerifyInvalidInput ( "Qc==" ) ; // last 4 bits != 0
317+ VerifyInvalidInput ( "Qd==" ) ; // last 4 bits != 0
318+ VerifyInvalidInput ( "Qe==" ) ; // last 4 bits != 0
319+ VerifyInvalidInput ( "Qf==" ) ; // last 4 bits != 0
320+ }
321+
322+ [ Fact ]
323+ public static void AcceptsValidUnusedBits ( )
324+ {
325+ // Valid cases with zero unused bits should continue to work
326+
327+ // No padding - all bits used
328+ string noPadding = "QUJD" ; // "ABC"
329+ byte [ ] result1 = Convert . FromBase64String ( noPadding ) ;
330+ Assert . Equal ( 3 , result1 . Length ) ;
331+
332+ // One padding - last 2 bits are 0
333+ string onePadding = "QUI=" ; // "AB"
334+ byte [ ] result2 = Convert . FromBase64String ( onePadding ) ;
335+ Assert . Equal ( 2 , result2 . Length ) ;
336+
337+ // Two padding - last 4 bits are 0
338+ string twoPadding = "QQ==" ; // "A"
339+ byte [ ] result3 = Convert . FromBase64String ( twoPadding ) ;
340+ Assert . Equal ( 1 , result3 . Length ) ;
341+ }
269342 }
270343}
0 commit comments