// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package base64 implements base64 encoding as specified by RFC 4648.
package base64 import ( ) /* * Encodings */ // An Encoding is a radix 64 encoding/decoding scheme, defined by a // 64-character alphabet. The most common encoding is the "base64" // encoding defined in RFC 4648 and used in MIME (RFC 2045) and PEM // (RFC 1421). RFC 4648 also defines an alternate encoding, which is // the standard encoding with - and _ substituted for + and /. type Encoding struct { encode [64]byte // mapping of symbol index to symbol byte value decodeMap [256]uint8 // mapping of symbol byte value to symbol index padChar rune strict bool } const ( StdPadding rune = '=' // Standard padding character NoPadding rune = -1 // No padding ) const ( decodeMapInitialize = "" + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" invalidIndex = '\xff' ) // NewEncoding returns a new padded Encoding defined by the given alphabet, // which must be a 64-byte string that contains unique byte values and // does not contain the padding character or CR / LF ('\r', '\n'). // The alphabet is treated as a sequence of byte values // without any special treatment for multi-byte UTF-8. // The resulting Encoding uses the default padding character ('='), // which may be changed or disabled via [Encoding.WithPadding]. func ( string) *Encoding { if len() != 64 { panic("encoding alphabet is not 64-bytes long") } := new(Encoding) .padChar = StdPadding copy(.encode[:], ) copy(.decodeMap[:], decodeMapInitialize) for := 0; < len(); ++ { // Note: While we document that the alphabet cannot contain // the padding character, we do not enforce it since we do not know // if the caller intends to switch the padding from StdPadding later. switch { case [] == '\n' || [] == '\r': panic("encoding alphabet contains newline character") case .decodeMap[[]] != invalidIndex: panic("encoding alphabet includes duplicate symbols") } .decodeMap[[]] = uint8() } return } // WithPadding creates a new encoding identical to enc except // with a specified padding character, or [NoPadding] to disable padding. // The padding character must not be '\r' or '\n', // must not be contained in the encoding's alphabet, // must not be negative, and must be a rune equal or below '\xff'. // Padding characters above '\x7f' are encoded as their exact byte value // rather than using the UTF-8 representation of the codepoint. func ( Encoding) ( rune) *Encoding { switch { case < NoPadding || == '\r' || == '\n' || > 0xff: panic("invalid padding") case != NoPadding && .decodeMap[byte()] != invalidIndex: panic("padding contained in alphabet") } .padChar = return & } // Strict creates a new encoding identical to enc except with // strict decoding enabled. In this mode, the decoder requires that // trailing padding bits are zero, as described in RFC 4648 section 3.5. // // Note that the input is still malleable, as new line characters // (CR and LF) are still ignored. func ( Encoding) () *Encoding { .strict = true return & } // StdEncoding is the standard base64 encoding, as defined in RFC 4648. var StdEncoding = NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") // URLEncoding is the alternate base64 encoding defined in RFC 4648. // It is typically used in URLs and file names. var URLEncoding = NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_") // RawStdEncoding is the standard raw, unpadded base64 encoding, // as defined in RFC 4648 section 3.2. // This is the same as [StdEncoding] but omits padding characters. var RawStdEncoding = StdEncoding.WithPadding(NoPadding) // RawURLEncoding is the unpadded alternate base64 encoding defined in RFC 4648. // It is typically used in URLs and file names. // This is the same as [URLEncoding] but omits padding characters. var RawURLEncoding = URLEncoding.WithPadding(NoPadding) /* * Encoder */ // Encode encodes src using the encoding enc, // writing [Encoding.EncodedLen](len(src)) bytes to dst. // // The encoding pads the output to a multiple of 4 bytes, // so Encode is not appropriate for use on individual blocks // of a large data stream. Use [NewEncoder] instead. func ( *Encoding) (, []byte) { if len() == 0 { return } // enc is a pointer receiver, so the use of enc.encode within the hot // loop below means a nil check at every operation. Lift that nil check // outside of the loop to speed up the encoder. _ = .encode , := 0, 0 := (len() / 3) * 3 for < { // Convert 3x 8bit source bytes into 4 bytes := uint([+0])<<16 | uint([+1])<<8 | uint([+2]) [+0] = .encode[>>18&0x3F] [+1] = .encode[>>12&0x3F] [+2] = .encode[>>6&0x3F] [+3] = .encode[&0x3F] += 3 += 4 } := len() - if == 0 { return } // Add the remaining small block := uint([+0]) << 16 if == 2 { |= uint([+1]) << 8 } [+0] = .encode[>>18&0x3F] [+1] = .encode[>>12&0x3F] switch { case 2: [+2] = .encode[>>6&0x3F] if .padChar != NoPadding { [+3] = byte(.padChar) } case 1: if .padChar != NoPadding { [+2] = byte(.padChar) [+3] = byte(.padChar) } } } // AppendEncode appends the base64 encoded src to dst // and returns the extended buffer. func ( *Encoding) (, []byte) []byte { := .EncodedLen(len()) = slices.Grow(, ) .Encode([len():][:], ) return [:len()+] } // EncodeToString returns the base64 encoding of src. func ( *Encoding) ( []byte) string { := make([]byte, .EncodedLen(len())) .Encode(, ) return string() } type encoder struct { err error enc *Encoding w io.Writer buf [3]byte // buffered data waiting to be encoded nbuf int // number of bytes in buf out [1024]byte // output buffer } func ( *encoder) ( []byte) ( int, error) { if .err != nil { return 0, .err } // Leading fringe. if .nbuf > 0 { var int for = 0; < len() && .nbuf < 3; ++ { .buf[.nbuf] = [] .nbuf++ } += = [:] if .nbuf < 3 { return } .enc.Encode(.out[:], .buf[:]) if _, .err = .w.Write(.out[:4]); .err != nil { return , .err } .nbuf = 0 } // Large interior chunks. for len() >= 3 { := len(.out) / 4 * 3 if > len() { = len() -= % 3 } .enc.Encode(.out[:], [:]) if _, .err = .w.Write(.out[0 : /3*4]); .err != nil { return , .err } += = [:] } // Trailing fringe. copy(.buf[:], ) .nbuf = len() += len() return } // Close flushes any pending output from the encoder. // It is an error to call Write after calling Close. func ( *encoder) () error { // If there's anything left in the buffer, flush it out if .err == nil && .nbuf > 0 { .enc.Encode(.out[:], .buf[:.nbuf]) _, .err = .w.Write(.out[:.enc.EncodedLen(.nbuf)]) .nbuf = 0 } return .err } // NewEncoder returns a new base64 stream encoder. Data written to // the returned writer will be encoded using enc and then written to w. // Base64 encodings operate in 4-byte blocks; when finished // writing, the caller must Close the returned encoder to flush any // partially written blocks. func ( *Encoding, io.Writer) io.WriteCloser { return &encoder{enc: , w: } } // EncodedLen returns the length in bytes of the base64 encoding // of an input buffer of length n. func ( *Encoding) ( int) int { if .padChar == NoPadding { return /3*4 + (%3*8+5)/6 // minimum # chars at 6 bits per char } return ( + 2) / 3 * 4 // minimum # 4-char quanta, 3 bytes each } /* * Decoder */ type CorruptInputError int64 func ( CorruptInputError) () string { return "illegal base64 data at input byte " + strconv.FormatInt(int64(), 10) } // decodeQuantum decodes up to 4 base64 bytes. The received parameters are // the destination buffer dst, the source buffer src and an index in the // source buffer si. // It returns the number of bytes read from src, the number of bytes written // to dst, and an error, if any. func ( *Encoding) (, []byte, int) (, int, error) { // Decode quantum using the base64 alphabet var [4]byte := 4 // Lift the nil check outside of the loop. _ = .decodeMap for := 0; < len(); ++ { if len() == { switch { case == 0: return , 0, nil case == 1, .padChar != NoPadding: return , 0, CorruptInputError( - ) } = break } := [] ++ := .decodeMap[] if != 0xff { [] = continue } if == '\n' || == '\r' { -- continue } if rune() != .padChar { return , 0, CorruptInputError( - 1) } // We've reached the end and there's padding switch { case 0, 1: // incorrect padding return , 0, CorruptInputError( - 1) case 2: // "==" is expected, the first "=" is already consumed. // skip over newlines for < len() && ([] == '\n' || [] == '\r') { ++ } if == len() { // not enough padding return , 0, CorruptInputError(len()) } if rune([]) != .padChar { // incorrect padding return , 0, CorruptInputError( - 1) } ++ } // skip over newlines for < len() && ([] == '\n' || [] == '\r') { ++ } if < len() { // trailing garbage = CorruptInputError() } = break } // Convert 4x 6bit source bytes into 3 bytes := uint([0])<<18 | uint([1])<<12 | uint([2])<<6 | uint([3]) [2], [1], [0] = byte(>>0), byte(>>8), byte(>>16) switch { case 4: [2] = [2] [2] = 0 fallthrough case 3: [1] = [1] if .strict && [2] != 0 { return , 0, CorruptInputError( - 1) } [1] = 0 fallthrough case 2: [0] = [0] if .strict && ([1] != 0 || [2] != 0) { return , 0, CorruptInputError( - 2) } } return , - 1, } // AppendDecode appends the base64 decoded src to dst // and returns the extended buffer. // If the input is malformed, it returns the partially decoded src and an error. // New line characters (\r and \n) are ignored. func ( *Encoding) (, []byte) ([]byte, error) { // Compute the output size without padding to avoid over allocating. := len() for > 0 && rune([-1]) == .padChar { -- } = decodedLen(, NoPadding) = slices.Grow(, ) , := .Decode([len():][:], ) return [:len()+], } // DecodeString returns the bytes represented by the base64 string s. // If the input is malformed, it returns the partially decoded data and // [CorruptInputError]. New line characters (\r and \n) are ignored. func ( *Encoding) ( string) ([]byte, error) { := make([]byte, .DecodedLen(len())) , := .Decode(, []byte()) return [:], } type decoder struct { err error readErr error // error from r.Read enc *Encoding r io.Reader buf [1024]byte // leftover input nbuf int out []byte // leftover decoded output outbuf [1024 / 4 * 3]byte } func ( *decoder) ( []byte) ( int, error) { // Use leftover decoded output from last read. if len(.out) > 0 { = copy(, .out) .out = .out[:] return , nil } if .err != nil { return 0, .err } // This code assumes that d.r strips supported whitespace ('\r' and '\n'). // Refill buffer. for .nbuf < 4 && .readErr == nil { := len() / 3 * 4 if < 4 { = 4 } if > len(.buf) { = len(.buf) } , .readErr = .r.Read(.buf[.nbuf:]) .nbuf += } if .nbuf < 4 { if .enc.padChar == NoPadding && .nbuf > 0 { // Decode final fragment, without padding. var int , .err = .enc.Decode(.outbuf[:], .buf[:.nbuf]) .nbuf = 0 .out = .outbuf[:] = copy(, .out) .out = .out[:] if > 0 || len() == 0 && len(.out) > 0 { return , nil } if .err != nil { return 0, .err } } .err = .readErr if .err == io.EOF && .nbuf > 0 { .err = io.ErrUnexpectedEOF } return 0, .err } // Decode chunk into p, or d.out and then p if p is too small. := .nbuf / 4 * 4 := .nbuf / 4 * 3 if > len() { , .err = .enc.Decode(.outbuf[:], .buf[:]) .out = .outbuf[:] = copy(, .out) .out = .out[:] } else { , .err = .enc.Decode(, .buf[:]) } .nbuf -= copy(.buf[:.nbuf], .buf[:]) return , .err } // Decode decodes src using the encoding enc. It writes at most // [Encoding.DecodedLen](len(src)) bytes to dst and returns the number of bytes // written. The caller must ensure that dst is large enough to hold all // the decoded data. If src contains invalid base64 data, it will return the // number of bytes successfully written and [CorruptInputError]. // New line characters (\r and \n) are ignored. func ( *Encoding) (, []byte) ( int, error) { if len() == 0 { return 0, nil } // Lift the nil check outside of the loop. enc.decodeMap is directly // used later in this function, to let the compiler know that the // receiver can't be nil. _ = .decodeMap := 0 for strconv.IntSize >= 64 && len()- >= 8 && len()- >= 8 { := [ : +8] if , := assemble64( .decodeMap[[0]], .decodeMap[[1]], .decodeMap[[2]], .decodeMap[[3]], .decodeMap[[4]], .decodeMap[[5]], .decodeMap[[6]], .decodeMap[[7]], ); { binary.BigEndian.PutUint64([:], ) += 6 += 8 } else { var int , , = .decodeQuantum([:], , ) += if != nil { return , } } } for len()- >= 4 && len()- >= 4 { := [ : +4] if , := assemble32( .decodeMap[[0]], .decodeMap[[1]], .decodeMap[[2]], .decodeMap[[3]], ); { binary.BigEndian.PutUint32([:], ) += 3 += 4 } else { var int , , = .decodeQuantum([:], , ) += if != nil { return , } } } for < len() { var int , , = .decodeQuantum([:], , ) += if != nil { return , } } return , } // assemble32 assembles 4 base64 digits into 3 bytes. // Each digit comes from the decode map, and will be 0xff // if it came from an invalid character. func assemble32(, , , byte) ( uint32, bool) { // Check that all the digits are valid. If any of them was 0xff, their // bitwise OR will be 0xff. if ||| == 0xff { return 0, false } return uint32()<<26 | uint32()<<20 | uint32()<<14 | uint32()<<8, true } // assemble64 assembles 8 base64 digits into 6 bytes. // Each digit comes from the decode map, and will be 0xff // if it came from an invalid character. func assemble64(, , , , , , , byte) ( uint64, bool) { // Check that all the digits are valid. If any of them was 0xff, their // bitwise OR will be 0xff. if ||||||| == 0xff { return 0, false } return uint64()<<58 | uint64()<<52 | uint64()<<46 | uint64()<<40 | uint64()<<34 | uint64()<<28 | uint64()<<22 | uint64()<<16, true } type newlineFilteringReader struct { wrapped io.Reader } func ( *newlineFilteringReader) ( []byte) (int, error) { , := .wrapped.Read() for > 0 { := 0 for , := range [:] { if != '\r' && != '\n' { if != { [] = } ++ } } if > 0 { return , } // Previous buffer entirely whitespace, read again , = .wrapped.Read() } return , } // NewDecoder constructs a new base64 stream decoder. func ( *Encoding, io.Reader) io.Reader { return &decoder{enc: , r: &newlineFilteringReader{}} } // DecodedLen returns the maximum length in bytes of the decoded data // corresponding to n bytes of base64-encoded data. func ( *Encoding) ( int) int { return decodedLen(, .padChar) } func decodedLen( int, rune) int { if == NoPadding { // Unpadded data may end with partial block of 2-3 characters. return /4*3 + %4*6/8 } // Padded base64 should always be a multiple of 4 characters in length. return / 4 * 3 }