1// Copyright 2009 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package base64 6 7import ( 8 "bytes" 9 "errors" 10 "fmt" 11 "io" 12 "math" 13 "reflect" 14 "runtime/debug" 15 "strconv" 16 "strings" 17 "testing" 18 "time" 19) 20 21type testpair struct { 22 decoded, encoded string 23} 24 25var pairs = []testpair{ 26 // RFC 3548 examples 27 {"\x14\xfb\x9c\x03\xd9\x7e", "FPucA9l+"}, 28 {"\x14\xfb\x9c\x03\xd9", "FPucA9k="}, 29 {"\x14\xfb\x9c\x03", "FPucAw=="}, 30 31 // RFC 4648 examples 32 {"", ""}, 33 {"f", "Zg=="}, 34 {"fo", "Zm8="}, 35 {"foo", "Zm9v"}, 36 {"foob", "Zm9vYg=="}, 37 {"fooba", "Zm9vYmE="}, 38 {"foobar", "Zm9vYmFy"}, 39 40 // Wikipedia examples 41 {"sure.", "c3VyZS4="}, 42 {"sure", "c3VyZQ=="}, 43 {"sur", "c3Vy"}, 44 {"su", "c3U="}, 45 {"leasure.", "bGVhc3VyZS4="}, 46 {"easure.", "ZWFzdXJlLg=="}, 47 {"asure.", "YXN1cmUu"}, 48 {"sure.", "c3VyZS4="}, 49} 50 51// Do nothing to a reference base64 string (leave in standard format) 52func stdRef(ref string) string { 53 return ref 54} 55 56// Convert a reference string to URL-encoding 57func urlRef(ref string) string { 58 ref = strings.ReplaceAll(ref, "+", "-") 59 ref = strings.ReplaceAll(ref, "/", "_") 60 return ref 61} 62 63// Convert a reference string to raw, unpadded format 64func rawRef(ref string) string { 65 return strings.TrimRight(ref, "=") 66} 67 68// Both URL and unpadding conversions 69func rawURLRef(ref string) string { 70 return rawRef(urlRef(ref)) 71} 72 73const encodeStd = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" 74 75// A nonstandard encoding with a funny padding character, for testing 76var funnyEncoding = NewEncoding(encodeStd).WithPadding(rune('@')) 77 78func funnyRef(ref string) string { 79 return strings.ReplaceAll(ref, "=", "@") 80} 81 82type encodingTest struct { 83 enc *Encoding // Encoding to test 84 conv func(string) string // Reference string converter 85} 86 87var encodingTests = []encodingTest{ 88 {StdEncoding, stdRef}, 89 {URLEncoding, urlRef}, 90 {RawStdEncoding, rawRef}, 91 {RawURLEncoding, rawURLRef}, 92 {funnyEncoding, funnyRef}, 93 {StdEncoding.Strict(), stdRef}, 94 {URLEncoding.Strict(), urlRef}, 95 {RawStdEncoding.Strict(), rawRef}, 96 {RawURLEncoding.Strict(), rawURLRef}, 97 {funnyEncoding.Strict(), funnyRef}, 98} 99 100var bigtest = testpair{ 101 "Twas brillig, and the slithy toves", 102 "VHdhcyBicmlsbGlnLCBhbmQgdGhlIHNsaXRoeSB0b3Zlcw==", 103} 104 105func testEqual(t *testing.T, msg string, args ...any) bool { 106 t.Helper() 107 if args[len(args)-2] != args[len(args)-1] { 108 t.Errorf(msg, args...) 109 return false 110 } 111 return true 112} 113 114func TestEncode(t *testing.T) { 115 for _, p := range pairs { 116 for _, tt := range encodingTests { 117 got := tt.enc.EncodeToString([]byte(p.decoded)) 118 testEqual(t, "Encode(%q) = %q, want %q", p.decoded, got, tt.conv(p.encoded)) 119 dst := tt.enc.AppendEncode([]byte("lead"), []byte(p.decoded)) 120 testEqual(t, `AppendEncode("lead", %q) = %q, want %q`, p.decoded, string(dst), "lead"+tt.conv(p.encoded)) 121 } 122 } 123} 124 125func TestEncoder(t *testing.T) { 126 for _, p := range pairs { 127 bb := &strings.Builder{} 128 encoder := NewEncoder(StdEncoding, bb) 129 encoder.Write([]byte(p.decoded)) 130 encoder.Close() 131 testEqual(t, "Encode(%q) = %q, want %q", p.decoded, bb.String(), p.encoded) 132 } 133} 134 135func TestEncoderBuffering(t *testing.T) { 136 input := []byte(bigtest.decoded) 137 for bs := 1; bs <= 12; bs++ { 138 bb := &strings.Builder{} 139 encoder := NewEncoder(StdEncoding, bb) 140 for pos := 0; pos < len(input); pos += bs { 141 end := pos + bs 142 if end > len(input) { 143 end = len(input) 144 } 145 n, err := encoder.Write(input[pos:end]) 146 testEqual(t, "Write(%q) gave error %v, want %v", input[pos:end], err, error(nil)) 147 testEqual(t, "Write(%q) gave length %v, want %v", input[pos:end], n, end-pos) 148 } 149 err := encoder.Close() 150 testEqual(t, "Close gave error %v, want %v", err, error(nil)) 151 testEqual(t, "Encoding/%d of %q = %q, want %q", bs, bigtest.decoded, bb.String(), bigtest.encoded) 152 } 153} 154 155func TestDecode(t *testing.T) { 156 for _, p := range pairs { 157 for _, tt := range encodingTests { 158 encoded := tt.conv(p.encoded) 159 dbuf := make([]byte, tt.enc.DecodedLen(len(encoded))) 160 count, err := tt.enc.Decode(dbuf, []byte(encoded)) 161 testEqual(t, "Decode(%q) = error %v, want %v", encoded, err, error(nil)) 162 testEqual(t, "Decode(%q) = length %v, want %v", encoded, count, len(p.decoded)) 163 testEqual(t, "Decode(%q) = %q, want %q", encoded, string(dbuf[0:count]), p.decoded) 164 165 dbuf, err = tt.enc.DecodeString(encoded) 166 testEqual(t, "DecodeString(%q) = error %v, want %v", encoded, err, error(nil)) 167 testEqual(t, "DecodeString(%q) = %q, want %q", encoded, string(dbuf), p.decoded) 168 169 dst, err := tt.enc.AppendDecode([]byte("lead"), []byte(encoded)) 170 testEqual(t, "AppendDecode(%q) = error %v, want %v", p.encoded, err, error(nil)) 171 testEqual(t, `AppendDecode("lead", %q) = %q, want %q`, p.encoded, string(dst), "lead"+p.decoded) 172 173 dst2, err := tt.enc.AppendDecode(dst[:0:len(p.decoded)], []byte(encoded)) 174 testEqual(t, "AppendDecode(%q) = error %v, want %v", p.encoded, err, error(nil)) 175 testEqual(t, `AppendDecode("", %q) = %q, want %q`, p.encoded, string(dst2), p.decoded) 176 if len(dst) > 0 && len(dst2) > 0 && &dst[0] != &dst2[0] { 177 t.Errorf("unexpected capacity growth: got %d, want %d", cap(dst2), cap(dst)) 178 } 179 } 180 } 181} 182 183func TestDecoder(t *testing.T) { 184 for _, p := range pairs { 185 decoder := NewDecoder(StdEncoding, strings.NewReader(p.encoded)) 186 dbuf := make([]byte, StdEncoding.DecodedLen(len(p.encoded))) 187 count, err := decoder.Read(dbuf) 188 if err != nil && err != io.EOF { 189 t.Fatal("Read failed", err) 190 } 191 testEqual(t, "Read from %q = length %v, want %v", p.encoded, count, len(p.decoded)) 192 testEqual(t, "Decoding of %q = %q, want %q", p.encoded, string(dbuf[0:count]), p.decoded) 193 if err != io.EOF { 194 _, err = decoder.Read(dbuf) 195 } 196 testEqual(t, "Read from %q = %v, want %v", p.encoded, err, io.EOF) 197 } 198} 199 200func TestDecoderBuffering(t *testing.T) { 201 for bs := 1; bs <= 12; bs++ { 202 decoder := NewDecoder(StdEncoding, strings.NewReader(bigtest.encoded)) 203 buf := make([]byte, len(bigtest.decoded)+12) 204 var total int 205 var n int 206 var err error 207 for total = 0; total < len(bigtest.decoded) && err == nil; { 208 n, err = decoder.Read(buf[total : total+bs]) 209 total += n 210 } 211 if err != nil && err != io.EOF { 212 t.Errorf("Read from %q at pos %d = %d, unexpected error %v", bigtest.encoded, total, n, err) 213 } 214 testEqual(t, "Decoding/%d of %q = %q, want %q", bs, bigtest.encoded, string(buf[0:total]), bigtest.decoded) 215 } 216} 217 218func TestDecodeCorrupt(t *testing.T) { 219 testCases := []struct { 220 input string 221 offset int // -1 means no corruption. 222 }{ 223 {"", -1}, 224 {"\n", -1}, 225 {"AAA=\n", -1}, 226 {"AAAA\n", -1}, 227 {"!!!!", 0}, 228 {"====", 0}, 229 {"x===", 1}, 230 {"=AAA", 0}, 231 {"A=AA", 1}, 232 {"AA=A", 2}, 233 {"AA==A", 4}, 234 {"AAA=AAAA", 4}, 235 {"AAAAA", 4}, 236 {"AAAAAA", 4}, 237 {"A=", 1}, 238 {"A==", 1}, 239 {"AA=", 3}, 240 {"AA==", -1}, 241 {"AAA=", -1}, 242 {"AAAA", -1}, 243 {"AAAAAA=", 7}, 244 {"YWJjZA=====", 8}, 245 {"A!\n", 1}, 246 {"A=\n", 1}, 247 } 248 for _, tc := range testCases { 249 dbuf := make([]byte, StdEncoding.DecodedLen(len(tc.input))) 250 _, err := StdEncoding.Decode(dbuf, []byte(tc.input)) 251 if tc.offset == -1 { 252 if err != nil { 253 t.Error("Decoder wrongly detected corruption in", tc.input) 254 } 255 continue 256 } 257 switch err := err.(type) { 258 case CorruptInputError: 259 testEqual(t, "Corruption in %q at offset %v, want %v", tc.input, int(err), tc.offset) 260 default: 261 t.Error("Decoder failed to detect corruption in", tc) 262 } 263 } 264} 265 266func TestDecodeBounds(t *testing.T) { 267 var buf [32]byte 268 s := StdEncoding.EncodeToString(buf[:]) 269 defer func() { 270 if err := recover(); err != nil { 271 t.Fatalf("Decode panicked unexpectedly: %v\n%s", err, debug.Stack()) 272 } 273 }() 274 n, err := StdEncoding.Decode(buf[:], []byte(s)) 275 if n != len(buf) || err != nil { 276 t.Fatalf("StdEncoding.Decode = %d, %v, want %d, nil", n, err, len(buf)) 277 } 278} 279 280func TestEncodedLen(t *testing.T) { 281 type test struct { 282 enc *Encoding 283 n int 284 want int64 285 } 286 tests := []test{ 287 {RawStdEncoding, 0, 0}, 288 {RawStdEncoding, 1, 2}, 289 {RawStdEncoding, 2, 3}, 290 {RawStdEncoding, 3, 4}, 291 {RawStdEncoding, 7, 10}, 292 {StdEncoding, 0, 0}, 293 {StdEncoding, 1, 4}, 294 {StdEncoding, 2, 4}, 295 {StdEncoding, 3, 4}, 296 {StdEncoding, 4, 8}, 297 {StdEncoding, 7, 12}, 298 } 299 // check overflow 300 switch strconv.IntSize { 301 case 32: 302 tests = append(tests, test{RawStdEncoding, (math.MaxInt-5)/8 + 1, 357913942}) 303 tests = append(tests, test{RawStdEncoding, math.MaxInt/4*3 + 2, math.MaxInt}) 304 case 64: 305 tests = append(tests, test{RawStdEncoding, (math.MaxInt-5)/8 + 1, 1537228672809129302}) 306 tests = append(tests, test{RawStdEncoding, math.MaxInt/4*3 + 2, math.MaxInt}) 307 } 308 for _, tt := range tests { 309 if got := tt.enc.EncodedLen(tt.n); int64(got) != tt.want { 310 t.Errorf("EncodedLen(%d): got %d, want %d", tt.n, got, tt.want) 311 } 312 } 313} 314 315func TestDecodedLen(t *testing.T) { 316 type test struct { 317 enc *Encoding 318 n int 319 want int64 320 } 321 tests := []test{ 322 {RawStdEncoding, 0, 0}, 323 {RawStdEncoding, 2, 1}, 324 {RawStdEncoding, 3, 2}, 325 {RawStdEncoding, 4, 3}, 326 {RawStdEncoding, 10, 7}, 327 {StdEncoding, 0, 0}, 328 {StdEncoding, 4, 3}, 329 {StdEncoding, 8, 6}, 330 } 331 // check overflow 332 switch strconv.IntSize { 333 case 32: 334 tests = append(tests, test{RawStdEncoding, math.MaxInt/6 + 1, 268435456}) 335 tests = append(tests, test{RawStdEncoding, math.MaxInt, 1610612735}) 336 case 64: 337 tests = append(tests, test{RawStdEncoding, math.MaxInt/6 + 1, 1152921504606846976}) 338 tests = append(tests, test{RawStdEncoding, math.MaxInt, 6917529027641081855}) 339 } 340 for _, tt := range tests { 341 if got := tt.enc.DecodedLen(tt.n); int64(got) != tt.want { 342 t.Errorf("DecodedLen(%d): got %d, want %d", tt.n, got, tt.want) 343 } 344 } 345} 346 347func TestBig(t *testing.T) { 348 n := 3*1000 + 1 349 raw := make([]byte, n) 350 const alpha = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 351 for i := 0; i < n; i++ { 352 raw[i] = alpha[i%len(alpha)] 353 } 354 encoded := new(bytes.Buffer) 355 w := NewEncoder(StdEncoding, encoded) 356 nn, err := w.Write(raw) 357 if nn != n || err != nil { 358 t.Fatalf("Encoder.Write(raw) = %d, %v want %d, nil", nn, err, n) 359 } 360 err = w.Close() 361 if err != nil { 362 t.Fatalf("Encoder.Close() = %v want nil", err) 363 } 364 decoded, err := io.ReadAll(NewDecoder(StdEncoding, encoded)) 365 if err != nil { 366 t.Fatalf("io.ReadAll(NewDecoder(...)): %v", err) 367 } 368 369 if !bytes.Equal(raw, decoded) { 370 var i int 371 for i = 0; i < len(decoded) && i < len(raw); i++ { 372 if decoded[i] != raw[i] { 373 break 374 } 375 } 376 t.Errorf("Decode(Encode(%d-byte string)) failed at offset %d", n, i) 377 } 378} 379 380func TestNewLineCharacters(t *testing.T) { 381 // Each of these should decode to the string "sure", without errors. 382 const expected = "sure" 383 examples := []string{ 384 "c3VyZQ==", 385 "c3VyZQ==\r", 386 "c3VyZQ==\n", 387 "c3VyZQ==\r\n", 388 "c3VyZ\r\nQ==", 389 "c3V\ryZ\nQ==", 390 "c3V\nyZ\rQ==", 391 "c3VyZ\nQ==", 392 "c3VyZQ\n==", 393 "c3VyZQ=\n=", 394 "c3VyZQ=\r\n\r\n=", 395 } 396 for _, e := range examples { 397 buf, err := StdEncoding.DecodeString(e) 398 if err != nil { 399 t.Errorf("Decode(%q) failed: %v", e, err) 400 continue 401 } 402 if s := string(buf); s != expected { 403 t.Errorf("Decode(%q) = %q, want %q", e, s, expected) 404 } 405 } 406} 407 408type nextRead struct { 409 n int // bytes to return 410 err error // error to return 411} 412 413// faultInjectReader returns data from source, rate-limited 414// and with the errors as written to nextc. 415type faultInjectReader struct { 416 source string 417 nextc <-chan nextRead 418} 419 420func (r *faultInjectReader) Read(p []byte) (int, error) { 421 nr := <-r.nextc 422 if len(p) > nr.n { 423 p = p[:nr.n] 424 } 425 n := copy(p, r.source) 426 r.source = r.source[n:] 427 return n, nr.err 428} 429 430// tests that we don't ignore errors from our underlying reader 431func TestDecoderIssue3577(t *testing.T) { 432 next := make(chan nextRead, 10) 433 wantErr := errors.New("my error") 434 next <- nextRead{5, nil} 435 next <- nextRead{10, wantErr} 436 next <- nextRead{0, wantErr} 437 d := NewDecoder(StdEncoding, &faultInjectReader{ 438 source: "VHdhcyBicmlsbGlnLCBhbmQgdGhlIHNsaXRoeSB0b3Zlcw==", // twas brillig... 439 nextc: next, 440 }) 441 errc := make(chan error, 1) 442 go func() { 443 _, err := io.ReadAll(d) 444 errc <- err 445 }() 446 select { 447 case err := <-errc: 448 if err != wantErr { 449 t.Errorf("got error %v; want %v", err, wantErr) 450 } 451 case <-time.After(5 * time.Second): 452 t.Errorf("timeout; Decoder blocked without returning an error") 453 } 454} 455 456func TestDecoderIssue4779(t *testing.T) { 457 encoded := `CP/EAT8AAAEF 458AQEBAQEBAAAAAAAAAAMAAQIEBQYHCAkKCwEAAQUBAQEBAQEAAAAAAAAAAQACAwQFBgcICQoLEAAB 459BAEDAgQCBQcGCAUDDDMBAAIRAwQhEjEFQVFhEyJxgTIGFJGhsUIjJBVSwWIzNHKC0UMHJZJT8OHx 460Y3M1FqKygyZEk1RkRcKjdDYX0lXiZfKzhMPTdePzRieUpIW0lcTU5PSltcXV5fVWZnaGlqa2xtbm 4619jdHV2d3h5ent8fX5/cRAAICAQIEBAMEBQYHBwYFNQEAAhEDITESBEFRYXEiEwUygZEUobFCI8FS 4620fAzJGLhcoKSQ1MVY3M08SUGFqKygwcmNcLSRJNUoxdkRVU2dGXi8rOEw9N14/NGlKSFtJXE1OT0 463pbXF1eX1VmZ2hpamtsbW5vYnN0dXZ3eHl6e3x//aAAwDAQACEQMRAD8A9VSSSSUpJJJJSkkkJ+Tj 4641kiy1jCJJDnAcCTykpKkuQ6p/jN6FgmxlNduXawwAzaGH+V6jn/R/wCt71zdn+N/qL3kVYFNYB4N 465ji6PDVjWpKp9TSXnvTf8bFNjg3qOEa2n6VlLpj/rT/pf567DpX1i6L1hs9Py67X8mqdtg/rUWbbf 466+gkp0kkkklKSSSSUpJJJJT//0PVUkkklKVLq3WMDpGI7KzrNjADtYNXvI/Mqr/Pd/q9W3vaxjnvM 467NaCXE9gNSvGPrf8AWS3qmba5jjsJhoB0DAf0NDf6sevf+/lf8Hj0JJATfWT6/dV6oXU1uOLQeKKn 468EQP+Hubtfe/+R7Mf/g7f5xcocp++Z11JMCJPgFBxOg7/AOuqDx8I/ikpkXkmSdU8mJIJA/O8EMAy 469j+mSARB/17pKVXYWHXjsj7yIex0PadzXMO1zT5KHoNA3HT8ietoGhgjsfA+CSnvvqh/jJtqsrwOv 4702b6NGNzXfTYexzJ+nU7/ALkf4P8Awv6P9KvTQQ4AgyDqCF85Pho3CTB7eHwXoH+LT65uZbX9X+o2 471bqbPb06551Y4 472` 473 encodedShort := strings.ReplaceAll(encoded, "\n", "") 474 475 dec := NewDecoder(StdEncoding, strings.NewReader(encoded)) 476 res1, err := io.ReadAll(dec) 477 if err != nil { 478 t.Errorf("ReadAll failed: %v", err) 479 } 480 481 dec = NewDecoder(StdEncoding, strings.NewReader(encodedShort)) 482 var res2 []byte 483 res2, err = io.ReadAll(dec) 484 if err != nil { 485 t.Errorf("ReadAll failed: %v", err) 486 } 487 488 if !bytes.Equal(res1, res2) { 489 t.Error("Decoded results not equal") 490 } 491} 492 493func TestDecoderIssue7733(t *testing.T) { 494 s, err := StdEncoding.DecodeString("YWJjZA=====") 495 want := CorruptInputError(8) 496 if !reflect.DeepEqual(want, err) { 497 t.Errorf("Error = %v; want CorruptInputError(8)", err) 498 } 499 if string(s) != "abcd" { 500 t.Errorf("DecodeString = %q; want abcd", s) 501 } 502} 503 504func TestDecoderIssue15656(t *testing.T) { 505 _, err := StdEncoding.Strict().DecodeString("WvLTlMrX9NpYDQlEIFlnDB==") 506 want := CorruptInputError(22) 507 if !reflect.DeepEqual(want, err) { 508 t.Errorf("Error = %v; want CorruptInputError(22)", err) 509 } 510 _, err = StdEncoding.Strict().DecodeString("WvLTlMrX9NpYDQlEIFlnDA==") 511 if err != nil { 512 t.Errorf("Error = %v; want nil", err) 513 } 514 _, err = StdEncoding.DecodeString("WvLTlMrX9NpYDQlEIFlnDB==") 515 if err != nil { 516 t.Errorf("Error = %v; want nil", err) 517 } 518} 519 520func BenchmarkEncodeToString(b *testing.B) { 521 data := make([]byte, 8192) 522 b.SetBytes(int64(len(data))) 523 for i := 0; i < b.N; i++ { 524 StdEncoding.EncodeToString(data) 525 } 526} 527 528func BenchmarkDecodeString(b *testing.B) { 529 sizes := []int{2, 4, 8, 64, 8192} 530 benchFunc := func(b *testing.B, benchSize int) { 531 data := StdEncoding.EncodeToString(make([]byte, benchSize)) 532 b.SetBytes(int64(len(data))) 533 b.ResetTimer() 534 for i := 0; i < b.N; i++ { 535 StdEncoding.DecodeString(data) 536 } 537 } 538 for _, size := range sizes { 539 b.Run(fmt.Sprintf("%d", size), func(b *testing.B) { 540 benchFunc(b, size) 541 }) 542 } 543} 544 545func BenchmarkNewEncoding(b *testing.B) { 546 b.SetBytes(int64(len(Encoding{}.decodeMap))) 547 for i := 0; i < b.N; i++ { 548 e := NewEncoding(encodeStd) 549 for _, v := range e.decodeMap { 550 _ = v 551 } 552 } 553} 554 555func TestDecoderRaw(t *testing.T) { 556 source := "AAAAAA" 557 want := []byte{0, 0, 0, 0} 558 559 // Direct. 560 dec1, err := RawURLEncoding.DecodeString(source) 561 if err != nil || !bytes.Equal(dec1, want) { 562 t.Errorf("RawURLEncoding.DecodeString(%q) = %x, %v, want %x, nil", source, dec1, err, want) 563 } 564 565 // Through reader. Used to fail. 566 r := NewDecoder(RawURLEncoding, bytes.NewReader([]byte(source))) 567 dec2, err := io.ReadAll(io.LimitReader(r, 100)) 568 if err != nil || !bytes.Equal(dec2, want) { 569 t.Errorf("reading NewDecoder(RawURLEncoding, %q) = %x, %v, want %x, nil", source, dec2, err, want) 570 } 571 572 // Should work with padding. 573 r = NewDecoder(URLEncoding, bytes.NewReader([]byte(source+"=="))) 574 dec3, err := io.ReadAll(r) 575 if err != nil || !bytes.Equal(dec3, want) { 576 t.Errorf("reading NewDecoder(URLEncoding, %q) = %x, %v, want %x, nil", source+"==", dec3, err, want) 577 } 578} 579